diff --git a/.gitignore b/.gitignore index 0675bc41ebf2703740d770a58ac3fd6a4544b952..3bc041c90812930bfb6e6c9ac90a3f36ba83e2be 100644 --- a/.gitignore +++ b/.gitignore @@ -40,5 +40,5 @@ node_modules out/ .vscode - +rls/ /parity.* diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f3a8a3f0c1e49fb1819e781f294fe97e88e92736..8f2304e5a8ff77130855b87669dabc7d5d6d56dd 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -7,13 +7,12 @@ variables: RUSTFLAGS: "" CARGOFLAGS: "" CI_SERVER_NAME: "GitLab CI" - LIBSSL: "libssl1.0.0 (>=1.0.0)" cache: key: "$CI_BUILD_STAGE-$CI_BUILD_REF_NAME" paths: - target/ untracked: true -linux-stable: +linux-amd64: stage: build image: parity/rust:gitlab-ci only: @@ -24,46 +23,13 @@ linux-stable: script: - rustup default stable # ARGUMENTS: 1. BUILD_PLATFORM (target for binaries) 2. PLATFORM (target for cargo) 3. ARC (architecture) 4. & 5. CC & CXX flags 6. binary identifier - - scripts/gitlab-build.sh x86_64-unknown-linux-gnu x86_64-unknown-linux-gnu amd64 gcc g++ ubuntu + - scripts/gitlab-build.sh x86_64-unknown-linux-gnu x86_64-unknown-linux-gnu amd64 gcc g++ linux tags: - rust-stable artifacts: paths: - parity.zip name: "stable-x86_64-unknown-linux-gnu_parity" -linux-stable-debian: - stage: build - image: parity/rust-debian:gitlab-ci - only: - - beta - - tags - - stable - - triggers - script: - - export LIBSSL="libssl1.1 (>=1.1.0)" - - scripts/gitlab-build.sh x86_64-unknown-debian-gnu x86_64-unknown-linux-gnu amd64 gcc g++ debian - tags: - - rust-debian - artifacts: - paths: - - parity.zip - name: "stable-x86_64-unknown-debian-gnu_parity" -linux-centos: - stage: build - image: parity/rust-centos:gitlab-ci - only: - - beta - - tags - - stable - - triggers - script: - - scripts/gitlab-build.sh x86_64-unknown-centos-gnu x86_64-unknown-linux-gnu x86_64 gcc g++ centos - tags: - - rust-centos - artifacts: - paths: - - parity.zip - name: "x86_64-unknown-centos-gnu_parity" linux-i686: stage: build image: parity/rust-i686:gitlab-ci @@ -73,13 +39,14 @@ linux-i686: - stable - triggers script: - - scripts/gitlab-build.sh i686-unknown-linux-gnu i686-unknown-linux-gnu i386 gcc g++ ubuntu + - scripts/gitlab-build.sh i686-unknown-linux-gnu i686-unknown-linux-gnu i386 gcc g++ linux tags: - rust-i686 artifacts: paths: - parity.zip name: "i686-unknown-linux-gnu" + allow_failure: true linux-armv7: stage: build image: parity/rust-armv7:gitlab-ci @@ -89,14 +56,15 @@ linux-armv7: - stable - triggers script: - - scripts/gitlab-build.sh armv7-unknown-linux-gnueabihf armv7-unknown-linux-gnueabihf armhf arm-linux-gnueabihf-gcc arm-linux-gnueabihf-g++ ubuntu + - scripts/gitlab-build.sh armv7-unknown-linux-gnueabihf armv7-unknown-linux-gnueabihf armhf arm-linux-gnueabihf-gcc arm-linux-gnueabihf-g++ linux tags: - rust-arm artifacts: paths: - parity.zip name: "armv7_unknown_linux_gnueabihf_parity" -linux-arm: + allow_failure: true +linux-armhf: stage: build image: parity/rust-arm:gitlab-ci only: @@ -105,13 +73,14 @@ linux-arm: - stable - triggers script: - - scripts/gitlab-build.sh arm-unknown-linux-gnueabihf arm-unknown-linux-gnueabihf armhf arm-linux-gnueabihf-gcc arm-linux-gnueabihf-g++ ubuntu + - scripts/gitlab-build.sh arm-unknown-linux-gnueabihf arm-unknown-linux-gnueabihf armhf arm-linux-gnueabihf-gcc arm-linux-gnueabihf-g++ linux tags: - rust-arm artifacts: paths: - parity.zip name: "arm-unknown-linux-gnueabihf_parity" + allow_failure: true linux-aarch64: stage: build image: parity/rust-arm64:gitlab-ci @@ -121,7 +90,7 @@ linux-aarch64: - stable - triggers script: - - scripts/gitlab-build.sh aarch64-unknown-linux-gnu aarch64-unknown-linux-gnu arm64 aarch64-linux-gnu-gcc aarch64-linux-gnu-g++ ubuntu + - scripts/gitlab-build.sh aarch64-unknown-linux-gnu aarch64-unknown-linux-gnu arm64 aarch64-linux-gnu-gcc aarch64-linux-gnu-g++ linux tags: - rust-arm artifacts: @@ -170,22 +139,41 @@ windows: - stable - triggers script: - - sh scripts/gitlab-build.sh x86_64-pc-windows-msvc x86_64-pc-windows-msvc installer "" "" windows + - sh scripts/gitlab-build.sh x86_64-pc-windows-msvc x86_64-pc-windows-msvc amd64 "" "" windows tags: - rust-windows artifacts: paths: - parity.zip name: "x86_64-pc-windows-msvc_parity" +android-armv7: + stage: build + image: parity/parity-android:latest + only: + - beta + - tags + - stable + - triggers + script: + - cargo build --target=armv7-linux-androideabi + tags: + - rust-arm + allow_failure: true + artifacts: + paths: + - parity.zip + name: "armv7-linux-androideabi_parity" docker-build: stage: build only: - tags + - beta + - stable - triggers before_script: - docker info script: - - if [ "$CI_BUILD_REF_NAME" == "beta-release" ]; then DOCKER_TAG="latest"; else DOCKER_TAG=$CI_BUILD_REF_NAME; fi + - if [ "$CI_BUILD_REF_NAME" == "master" ]; then DOCKER_TAG="latest"; else DOCKER_TAG=$CI_BUILD_REF_NAME; fi - echo "Tag:" $DOCKER_TAG - docker login -u $Docker_Hub_User_Parity -p $Docker_Hub_Pass_Parity - scripts/docker-build.sh $DOCKER_TAG diff --git a/CHANGELOG.md b/CHANGELOG.md index c0f438696b66e7611bee7bfee252ccfcdb0193d0..25e2a28a57b91b60fc76181ec4e9983cae37ce1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,206 +1,450 @@ -## Parity [v1.10.0](https://github.com/paritytech/parity/releases/tag/v1.10.0) (2018-03-22) +## Parity [v1.11.3](https://github.com/paritytech/parity/releases/tag/v1.11.3) (2018-06-06) -This is the Parity 1.10.0-beta release! Cool! +Parity 1.11.3 is a security-relevant release. Please upgrade your nodes as soon as possible to [v1.10.6](https://github.com/paritytech/parity/releases/tag/v1.10.6) or [v1.11.3](https://github.com/paritytech/parity/releases/tag/v1.11.3). -### Disabling the Parity Wallet - -The **Parity Wallet (a.k.a. "UI") is now disabled by default**. We are preparing to split the wallet from the core client. - -To reactivate the parity wallet, you have to run Parity either with `parity --force-ui` (not recommended) or `parity ui` (deprecated) from the command line. Or, if you feel super fancy and want to test our pre-releases of the stand-alone electron wallet, head over to the [Parity-JS repositories and check the releases](https://github.com/Parity-JS/shell/releases). - -Further reading: - -- [Docs: Parity Wallet](https://wiki.parity.io/Parity-Wallet) -- [Docs: How to customize Parity UI?](https://wiki.parity.io/FAQ-Customize-Parity-UI.html) -- [Github: Parity-JS](https://github.com/parity-js) - -### Introducing the Wasm VM - -We are excited to announce support for **Wasm Smart Contracts on Kovan network**. The hard-fork to activate the Wasm-VM will take place on block `6_600_000`. - -To enable Wasm contracts on your custom network, just schedule a `wasmActivationTransition` at your favorite block number (e.g., `42`, `666`, or `0xbada55`). To hack your first Wasm smart contracts in Rust, have a look at the [Parity Wasm Tutorials](https://github.com/paritytech/pwasm-tutorial). - -Further reading: - -- [Docs: WebAssembly (wasm)](https://wiki.parity.io/WebAssembly-Home) -- [Docs: Wasm VM Design](https://wiki.parity.io/WebAssembly-Design) -- [Docs: Wasm tutorials and examples](https://wiki.parity.io/WebAssembly-Links) +The full list of included changes: -### Empty step messages in PoA +- Parity-version: bump beta to 1.11.3 ([#8806](https://github.com/paritytech/parity/pull/8806)) + - Parity-version: bump beta to 1.11.3 + - Disallow unsigned transactions in case EIP-86 is disabled ([#8802](https://github.com/paritytech/parity/pull/8802)) + - Fix ancient blocks queue deadlock ([#8751](https://github.com/paritytech/parity/pull/8751)) +- Update shell32-sys to fix windows build ([#8792](https://github.com/paritytech/parity/pull/8792)) +- Backports ([#8785](https://github.com/paritytech/parity/pull/8785)) + - Fix light sync with initial validator-set contract ([#8528](https://github.com/paritytech/parity/pull/8528)) + - Fix #8468 + - Use U256::max_value() instead + - Also change initial transaction gas + - Resumable warp-sync / Seed downloaded snapshots ([#8544](https://github.com/paritytech/parity/pull/8544)) + - Start dividing sync chain : first supplier method + - WIP - updated chain sync supplier + - Finish refactoring the Chain Sync Supplier + - Create Chain Sync Requester + - Add Propagator for Chain Sync + - Add the Chain Sync Handler + - Move tests from mod -> handler + - Move tests to propagator + - Refactor SyncRequester arguments + - Refactoring peer fork header handler + - Fix wrong highest block number in snapshot sync + - Small refactor... + - Resume warp-sync downloaded chunks + - Refactoring the previous chunks import + - Address PR grumbles + - Fix not seeding current snapshot + - Update SnapshotService readiness check + - Early abort importing previous chunks + - Update Gitlab CI config + - SyncState back to Waiting when Manifest peers disconnect + - Revert GitLab CI changes + - Refactor resuming snapshots + - Revert "Refactor resuming snapshots" + - Update informant log + - Refactor resuming snapshots + - Update informant message : show chunks done + - Don't open Browser post-install on Mac ([#8641](https://github.com/paritytech/parity/pull/8641)) + - Fix not downloading old blocks ([#8642](https://github.com/paritytech/parity/pull/8642)) + - Fix PoW blockchains sealing notifications in chain_new_blocks ([#8656](https://github.com/paritytech/parity/pull/8656)) + - Shutdown the Snapshot Service early ([#8658](https://github.com/paritytech/parity/pull/8658)) + - Shutdown the Snapshot Service when shutting down the runner + - Rename `service` to `client_service` + - Fix tests + - Fix cli signer ([#8682](https://github.com/paritytech/parity/pull/8682)) + - Update ethereum-types so `{:#x}` applies 0x prefix + - Set the request index to that of the current request ([#8683](https://github.com/paritytech/parity/pull/8683)) + - Set the request index to that of the current request + - Network-devp2p: handle UselessPeer disconnect ([#8686](https://github.com/paritytech/parity/pull/8686)) + - Fix local transactions policy. ([#8691](https://github.com/paritytech/parity/pull/8691)) + - CI: Fixes for Android Pipeline ([#8745](https://github.com/paritytech/parity/pull/8745)) + - Ci: Remove check for shared libraries in gitlab script + - Ci: allow android arm build to fail + - Custom Error Messages on ENFILE and EMFILE IO Errors ([#8744](https://github.com/paritytech/parity/pull/8744)) + - Custom Error Messages on ENFILE and EMFILE IO Errors + - Use assert-matches for more readable tests + - Fix Wording and consistency + - Ethcore-sync: fix connection to peers behind chain fork block ([#8710](https://github.com/paritytech/parity/pull/8710)) +- Parity-version: bump beta to 1.11.2 ([#8750](https://github.com/paritytech/parity/pull/8750)) + - Parity-version: bump beta to 1.11.2 + - Parity-version: unset critical flag -To **reduce blockchain bloat, proof-of-authority networks can now enable _empty step messages_ which replace empty blocks**. Each step message will be signed and broadcasted by the issuing authorities, and included and rewarded in the next non-empty block. +## Parity [v1.11.1](https://github.com/paritytech/parity/releases/tag/v1.11.1) (2018-05-15) -To enable empty step messages, set the `emptyStepsTransition` to your favorite block number. You can also specify a maximum number of empty steps with `maximumEmptySteps` in your chain spec. +This is the Parity 1.11.1-beta release! Hurray! -### Other noteworthy changes +Notable changes in reversed alphabetical order: -We removed the old database migrations from 2016. In case you upgrade Parity from a really, really old version, you will have to reset your database manually first with `parity db kill`. +- TOOLING: **Whisper CLI** [#8201](https://github.com/paritytech/parity/pull/8201) + - `whisper-cli` is a standalone tool to communicate with the Whisper protocol. + - It provides functionality to specify `whisper-pool-size`, `port` and `address` to use. + - All whisper RPC APIs are enabled and can be directly accessed. +- JSON-RPC API: **Return error in case eth_call returns VM errors** [#8448](https://github.com/paritytech/parity/pull/8448) + - This changes the behaviors of `eth_call` to respect VM errors if any. + - In case of `REVERT`, it will also return the reverted return data in hex format. +- ENGINES: **Block Reward Contract** [#8419](https://github.com/paritytech/parity/pull/8419) + - The _AuRa_ PoA engine has now support for having a contract to calculate the block rewards. + - The engine passes a list of benefactors and reward types to the contract which then returns a list of addresses and respective rewards. +- CORE: **Private Transactions** [#6422](https://github.com/paritytech/parity/pull/6422) + - Parity now provides a private transactions system. + - Please, check out our wiki to get an [overview and setup instructions](https://wiki.parity.io/Private-Transactions.html). +- CORE: **New Transaction Queue implementation** [#8074](https://github.com/paritytech/parity/pull/8074) + - Verification is now done in parallel. + - Previous queue had `O(1)` time to get pending set, but `O(n^2)` insertion time. And obviously insertion/removal happens much more often than retrieving the pending set (only for propagation and pending block building) Currently we have `O(n * log(senders))` pending set time (with cache) and `O(tx_per_sender)` (usually within `log(tx_per_sender)`) insertion time. + - `Scoring` and `Readiness` are separated from the pool, so it's easier to customize them or introduce different definitions (for instance for [EIP-859](https://github.com/ethereum/EIPs/issues/859) or private transactions, etc). + - Banning removed, soft-penalization introduced instead: if transaction exceeds the limit other transactions from that sender get lower priority. + - There is no explicit distinction between current and future transactions in the pool - `Readiness` determines that. Because of this we additionally remove `future` transactions that occupy the pool for long time. +- CONFIGURATION: **Warp-only sync with --warp-barrier [block-number] flag.** [#8228](https://github.com/paritytech/parity/pull/8228) + - Enables warp-only sync in case `--warp-barrier [block-number]` is provided. + - This avoids clients to warp to outdated snapshots that are too far away from the best block. + - This avoids clients to fall back to normal sync if there are no recent snapshots available currently. +- CONFIGURATION: **Disable UI by default.** [#8105](https://github.com/paritytech/parity/pull/8105) + - The user interface is now disabled by default. It still can be activated with the `--force-ui` flag. + - To get the stand-alone Parity UI, please check the dedicated [releases page](https://github.com/parity-js/shell/releases). +- CONFIGURATION: **Auto-updater improvements** [#8078](https://github.com/paritytech/parity/pull/8078) + - Added `--auto-update-delay` to randomly delay updates by `n` blocks. This takes into account the number of the block of the update release (old updates aren't delayed). + - Added `--auto-update-check-frequency` to define the periodicity of auto-update checks in number of blocks. + - This is an important improvement to ensure the network does not update all clients at the same time. +- CHAIN SPECS: **Enable WebAssembly and Byzantium for Ellaism** [#8520](https://github.com/paritytech/parity/pull/8520) + - This activates the Ellaism Byzantium hardfork ([2018-0004-byzantium](https://github.com/ellaism/specs/blob/master/specs/2018-0004-byzantium.md)) at block `2_000_000`. + - This enables the Wasm VM on Ellaism ([2018-0003-wasm-hardfork](https://github.com/ellaism/specs/blob/master/specs/2018-0003-wasm-hardfork.md)) at block `2_000_000`. + - Please, upgrade your clients if you run an Ellaism configuration. +- CHAIN SPECS: **Dev chain - increase gasLimit to 8_000_000** [#8362](https://github.com/paritytech/parity/pull/8362) + - This increases the default block gas limit on development chains to `8_000_000`. + - Please note, this makes previous dev chain configurations incompatible. +- CHAIN SPECS: **Add MCIP-6 Byzyantium transition to Musicoin spec** [#7841](https://github.com/paritytech/parity/pull/7841) + - This activates the Musicoin Byzantium hardfork ([MCIP-6](https://github.com/Musicoin/MCIPs/blob/master/MCIPS/mcip-6.md)) at block `2_222_222`. + - Please, upgrade your clients if you run a Musicoin configuration. -We fixed DELEGATECALL `from` and `to` fields, see [#7166](https://github.com/paritytech/parity/issues/7166). +The full list of included changes: -We reduced the default USD per transaction value to 0.0001. Thanks, @MysticRyuujin! +- Backports ([#8624](https://github.com/paritytech/parity/pull/8624)) + - Trace precompiled contracts when the transfer value is not zero ([#8486](https://github.com/paritytech/parity/pull/8486)) + - Trace precompiled contracts when the transfer value is not zero + - Add tests for precompiled CALL tracing + - Use byzantium test machine for the new test + - Add notes in comments on why we don't trace all precompiles + - Use is_transferred instead of transferred + - Return error if RLP size of transaction exceeds the limit ([#8473](https://github.com/paritytech/parity/pull/8473)) + - Return error if RLP size of transaction exceeds the limit + - Review comments fixed + - RLP check moved to verifier, corresponding pool test added + - Don't block sync when importing old blocks ([#8530](https://github.com/paritytech/parity/pull/8530)) + - Alter IO queueing. + - Don't require IoMessages to be Clone + - Ancient blocks imported via IoChannel. + - Get rid of private transactions io message. + - Get rid of deadlock and fix disconnected handler. + - Revert to old disconnect condition. + - Fix tests. + - Fix deadlock. + - Refactoring `ethcore-sync` - Fixing warp-sync barrier ([#8543](https://github.com/paritytech/parity/pull/8543)) + - Start dividing sync chain : first supplier method + - WIP - updated chain sync supplier + - Finish refactoring the Chain Sync Supplier + - Create Chain Sync Requester + - Add Propagator for Chain Sync + - Add the Chain Sync Handler + - Move tests from mod -> handler + - Move tests to propagator + - Refactor SyncRequester arguments + - Refactoring peer fork header handler + - Fix wrong highest block number in snapshot sync + - Small refactor... + - Address PR grumbles + - Retry failed CI job + - Fix tests + - PR Grumbles + - Handle socket address parsing errors ([#8545](https://github.com/paritytech/parity/pull/8545)) + - Fix packet count when talking with PAR2 peers ([#8555](https://github.com/paritytech/parity/pull/8555)) + - Support diferent packet counts in different protocol versions. + - Fix light timeouts and eclipse protection. + - Fix devp2p tests. + - Fix whisper-cli compilation. + - Fix compilation. + - Fix ethcore-sync tests. + - Revert "Fix light timeouts and eclipse protection." + - Increase timeouts. + - Add whisper CLI to the pipelines ([#8578](https://github.com/paritytech/parity/pull/8578)) + - Add whisper CLI to the pipelines + - Address todo, ref [#8579](https://github.com/paritytech/parity/pull/8579) + - Rename `whisper-cli binary` to `whisper` ([#8579](https://github.com/paritytech/parity/pull/8579)) + - Rename whisper-cli binary to whisper + - Fix tests + - Remove manually added text to the errors ([#8595](https://github.com/paritytech/parity/pull/8595)) + - Fix account list double 0x display ([#8596](https://github.com/paritytech/parity/pull/8596)) + - Remove unused self import + - Fix account list double 0x display + - Fix BlockReward contract "arithmetic operation overflow" ([#8611](https://github.com/paritytech/parity/pull/8611)) + - Fix BlockReward contract "arithmetic operation overflow" + - Add docs on how execute_as_system works + - Fix typo + - Rlp decode returns Result ([#8527](https://github.com/paritytech/parity/pull/8527)) + - Remove expect ([#8536](https://github.com/paritytech/parity/pull/8536)) + - Remove expect and propagate rlp::DecoderErrors as TrieErrors + - Decoding headers can fail ([#8570](https://github.com/paritytech/parity/pull/8570)) + - Rlp::decode returns Result + - Fix journaldb to handle rlp::decode Result + - Fix ethcore to work with rlp::decode returning Result + - Light client handles rlp::decode returning Result + - Fix tests in rlp_derive + - Fix tests + - Cleanup + - Cleanup + - Allow panic rather than breaking out of iterator + - Let decoding failures when reading from disk blow up + - Syntax + - Fix the trivial grumbles + - Fix failing tests + - Make Account::from_rlp return Result + - Syntx, sigh + - Temp-fix for decoding failures + - Header::decode returns Result + - Do not continue reading from the DB when a value could not be read + - Fix tests + - Handle header decoding in light_sync + - Handling header decoding errors + - Let the DecodeError bubble up unchanged + - Remove redundant error conversion + - Fix compiler warning ([#8590](https://github.com/paritytech/parity/pull/8590)) + - Attempt to fix intermittent test failures ([#8584](https://github.com/paritytech/parity/pull/8584)) + - Block_header can fail so return Result ([#8581](https://github.com/paritytech/parity/pull/8581)) + - Block_header can fail so return Result + - Restore previous return type based on feedback + - Fix failing doc tests running on non-code + - Block::decode() returns Result ([#8586](https://github.com/paritytech/parity/pull/8586)) + - Gitlab test script fixes ([#8573](https://github.com/paritytech/parity/pull/8573)) + - Exclude /docs from modified files. + - Ensure all references in the working tree are available + - Remove duplicated line from test script +- Bump beta to 1.11.1 ([#8627](https://github.com/paritytech/parity/pull/8627)) -The Musicoin chain is now enabled with Byzantium features starting at block `2_222_222`. +## Parity [v1.11.0](https://github.com/paritytech/parity/releases/tag/v1.11.0) (2018-05-09) -### Overview of all changes included +This is the Parity 1.11.0-beta release! ~~Hurray!~~ This release has been pulled due to peering issues, please use 1.11.1-beta. The full list of included changes: -- Re-enable signer, even with no UI. ([#8167](https://github.com/paritytech/parity/pull/8167)) ([#8168](https://github.com/paritytech/parity/pull/8168)) - - Re-enable signer, even with no UI. - - Fix message. -- Beta Backports ([#8136](https://github.com/paritytech/parity/pull/8136)) - - Support parity protocol. ([#8035](https://github.com/paritytech/parity/pull/8035)) - - updater: apply exponential backoff after download failure ([#8059](https://github.com/paritytech/parity/pull/8059)) - - updater: apply exponential backoff after download failure - - updater: reset backoff on new release - - Max code size on Kovan ([#8067](https://github.com/paritytech/parity/pull/8067)) - - Enable code size limit on kovan - - Fix formatting. - - Limit incoming connections. ([#8060](https://github.com/paritytech/parity/pull/8060)) - - Limit ingress connections - - Optimized handshakes logging - - WASM libraries bump ([#7970](https://github.com/paritytech/parity/pull/7970)) - - update wasmi, parity-wasm, wasm-utils to latest version - - Update to new wasmi & error handling - - also utilize new stack limiter - - fix typo - - replace dependency url - - Cargo.lock update - - add some dos protection ([#8084](https://github.com/paritytech/parity/pull/8084)) - - revert removing blooms ([#8066](https://github.com/paritytech/parity/pull/8066)) - - Revert "fix traces, removed bloomchain crate, closes [#7228](https://github.com/paritytech/parity/issues/7228), closes [#7167](https://github.com/paritytech/parity/issues/7167)" - - Revert "fixed broken logs ([#7934](https://github.com/paritytech/parity/pull/7934))" - - fixed broken logs - - bring back old lock order - - remove migration v13 - - revert CURRENT_VERSION to 12 in migration.rs - - more dos protection ([#8104](https://github.com/paritytech/parity/pull/8104)) - - Const time comparison ([#8113](https://github.com/paritytech/parity/pull/8113)) - - Use `subtle::slices_equal` for constant time comparison. - - Also update the existing version of subtle in `ethcrypto` from 0.1 to 0.5 - - Test specifically for InvalidPassword error. - - fix trace filter returning returning unrelated reward calls, closes #8070 ([#8098](https://github.com/paritytech/parity/pull/8098)) - - network: init discovery using healthy nodes ([#8061](https://github.com/paritytech/parity/pull/8061)) - - network: init discovery using healthy nodes - - network: fix style grumble - - network: fix typo - - Postpone Kovan hard fork ([#8137](https://github.com/paritytech/parity/pull/8137)) - - ethcore: postpone Kovan hard fork - - util: update version fork metadata - - Disable UI by default. ([#8105](https://github.com/paritytech/parity/pull/8105)) - - dapps: update parity-ui dependencies ([#8160](https://github.com/paritytech/parity/pull/8160)) -- Probe changes one step deeper ([#8134](https://github.com/paritytech/parity/pull/8134)) ([#8135](https://github.com/paritytech/parity/pull/8135)) -- Beta backports ([#8053](https://github.com/paritytech/parity/pull/8053)) - - CI: Fix cargo cache ([#7968](https://github.com/paritytech/parity/pull/7968)) - - Fix cache - - Only clean locked cargo cache on windows - - fixed ethstore sign ([#8026](https://github.com/paritytech/parity/pull/8026)) - - fixed parsing ethash seals and verify_block_undordered ([#8031](https://github.com/paritytech/parity/pull/8031)) - - fix for verify_block_basic crashing on invalid transaction rlp ([#8032](https://github.com/paritytech/parity/pull/8032)) - - fix cache & snapcraft CI build ([#8052](https://github.com/paritytech/parity/pull/8052)) - - Add MCIP-6 Byzyantium transition to Musicoin spec ([#7841](https://github.com/paritytech/parity/pull/7841)) - - Add test chain spec for musicoin byzantium testnet - - Add MCIP-6 Byzyantium transition to Musicoin spec - - Update mcip6_byz.json - - ethcore: update musicoin byzantium block number - - ethcore: update musicoin bootnodes - - Update musicoin.json - - More bootnodes. -- Make 1.10 beta ([#8022](https://github.com/paritytech/parity/pull/8022)) - - Make 1.10 beta - - Fix gitlab builds -- SecretStore: secretstore_generateDocumentKey RPC ([#7864](https://github.com/paritytech/parity/pull/7864)) -- SecretStore: ECDSA session for cases when 2*t < N ([#7739](https://github.com/paritytech/parity/pull/7739)) -- bump tiny-keccak ([#8019](https://github.com/paritytech/parity/pull/8019)) -- Remove un-necessary comment ([#8014](https://github.com/paritytech/parity/pull/8014)) -- clean up account fmt::Debug ([#7983](https://github.com/paritytech/parity/pull/7983)) -- improve quality of vote_collector module ([#7984](https://github.com/paritytech/parity/pull/7984)) -- ExecutedBlock cleanup ([#7991](https://github.com/paritytech/parity/pull/7991)) -- Hardware-wallet/usb-subscribe-refactor ([#7860](https://github.com/paritytech/parity/pull/7860)) -- remove wildcard imports from views, make tests more idiomatic ([#7986](https://github.com/paritytech/parity/pull/7986)) -- moved PerfTimer to a separate crate - "trace-time" ([#7985](https://github.com/paritytech/parity/pull/7985)) -- clean up ethcore::spec module imports ([#7990](https://github.com/paritytech/parity/pull/7990)) -- rpc: don't include current block in new_block_filter ([#7982](https://github.com/paritytech/parity/pull/7982)) -- fix traces, removed bloomchain crate ([#7979](https://github.com/paritytech/parity/pull/7979)) -- simplify compression and move it out of rlp crate ([#7957](https://github.com/paritytech/parity/pull/7957)) -- removed old migrations ([#7974](https://github.com/paritytech/parity/pull/7974)) -- Reject too large packets in snapshot sync. ([#7977](https://github.com/paritytech/parity/pull/7977)) -- fixed broken logs ([#7934](https://github.com/paritytech/parity/pull/7934)) -- Increase max download limit to 128MB ([#7965](https://github.com/paritytech/parity/pull/7965)) -- Calculate proper keccak256/sha3 using parity. ([#7953](https://github.com/paritytech/parity/pull/7953)) -- Add changelog for 1.8.10 stable and 1.9.3 beta ([#7947](https://github.com/paritytech/parity/pull/7947)) -- kvdb-rocksdb: remove buffered operations when committing transaction ([#7950](https://github.com/paritytech/parity/pull/7950)) -- Bump WebSockets ([#7952](https://github.com/paritytech/parity/pull/7952)) -- removed redundant Bloom conversions ([#7932](https://github.com/paritytech/parity/pull/7932)) -- simplify RefInfo fmt ([#7929](https://github.com/paritytech/parity/pull/7929)) -- Kovan WASM fork code ([#7849](https://github.com/paritytech/parity/pull/7849)) -- bring back trie and triehash benches ([#7926](https://github.com/paritytech/parity/pull/7926)) -- removed redundant PodAccount::new method ([#7928](https://github.com/paritytech/parity/pull/7928)) -- removed dummy wrapper structure - LogGroupPosition ([#7922](https://github.com/paritytech/parity/pull/7922)) -- spec: Validate required divisor fields are not 0 ([#7933](https://github.com/paritytech/parity/pull/7933)) -- simplify Client::filter_traces method ([#7936](https://github.com/paritytech/parity/pull/7936)) -- gitlab cache ([#7921](https://github.com/paritytech/parity/pull/7921)) -- Fix a division by zero in light client RPC handler ([#7917](https://github.com/paritytech/parity/pull/7917)) -- triehash optimisations ([#7920](https://github.com/paritytech/parity/pull/7920)) -- removed redundant Blockchain::db method ([#7919](https://github.com/paritytech/parity/pull/7919)) -- removed redundant Blockchain::rewind method ([#7918](https://github.com/paritytech/parity/pull/7918)) -- Pending transactions subscription ([#7906](https://github.com/paritytech/parity/pull/7906)) -- removed redundant otry! macro from ethcore ([#7916](https://github.com/paritytech/parity/pull/7916)) -- Make block generator easier to use ([#7888](https://github.com/paritytech/parity/pull/7888)) -- ECIP 1041 - Remove Difficulty Bomb ([#7905](https://github.com/paritytech/parity/pull/7905)) -- Fix CSP for dapps that require eval. ([#7867](https://github.com/paritytech/parity/pull/7867)) -- Fix gitlab ([#7901](https://github.com/paritytech/parity/pull/7901)) -- Gitlb snap master patch ([#7900](https://github.com/paritytech/parity/pull/7900)) -- fix snap build master ([#7896](https://github.com/paritytech/parity/pull/7896)) -- Fix wallet import ([#7873](https://github.com/paritytech/parity/pull/7873)) -- Fix snapcraft nightly ([#7884](https://github.com/paritytech/parity/pull/7884)) -- Add a timeout for light client sync requests ([#7848](https://github.com/paritytech/parity/pull/7848)) -- SecretStore: fixed test ([#7878](https://github.com/paritytech/parity/pull/7878)) -- Fix checksums and auto-update push ([#7846](https://github.com/paritytech/parity/pull/7846)) -- Forward-port snap fixes ([#7831](https://github.com/paritytech/parity/pull/7831)) -- Update gitlab-test.sh ([#7883](https://github.com/paritytech/parity/pull/7883)) -- Fix installer binary names for macos and windows ([#7881](https://github.com/paritytech/parity/pull/7881)) -- Fix string typo: "develoopment" -> "development" ([#7874](https://github.com/paritytech/parity/pull/7874)) -- Update the instructions to install the stable snap ([#7876](https://github.com/paritytech/parity/pull/7876)) -- SecretStore: 'broadcast' decryption session ([#7843](https://github.com/paritytech/parity/pull/7843)) -- Flush keyfiles. Resolves #7632 ([#7868](https://github.com/paritytech/parity/pull/7868)) -- Read registry_address from given block ([#7866](https://github.com/paritytech/parity/pull/7866)) -- Clean up docs formatting for Wasm runtime ([#7869](https://github.com/paritytech/parity/pull/7869)) -- WASM: Disable internal memory ([#7842](https://github.com/paritytech/parity/pull/7842)) -- Update gitlab-build.sh ([#7855](https://github.com/paritytech/parity/pull/7855)) -- ethabi version 5 ([#7723](https://github.com/paritytech/parity/pull/7723)) -- Light client: randomize the peer we dispatch requests to ([#7844](https://github.com/paritytech/parity/pull/7844)) -- Store updater metadata in a single place ([#7832](https://github.com/paritytech/parity/pull/7832)) -- Add new EF ropstens nodes. ([#7824](https://github.com/paritytech/parity/pull/7824)) -- refactor stratum to remove retain cycle ([#7827](https://github.com/paritytech/parity/pull/7827)) -- Bump jsonrpc. ([#7828](https://github.com/paritytech/parity/pull/7828)) -- Add binary identifiers and sha256sum to builds ([#7830](https://github.com/paritytech/parity/pull/7830)) -- Update references to UI shell & wallet ([#7808](https://github.com/paritytech/parity/pull/7808)) -- Adjust storage update evm-style ([#7812](https://github.com/paritytech/parity/pull/7812)) -- Updated WASM Runtime & new interpreter (wasmi) ([#7796](https://github.com/paritytech/parity/pull/7796)) -- SecretStore: ignore removed authorities when running auto-migration ([#7674](https://github.com/paritytech/parity/pull/7674)) -- Fix build ([#7807](https://github.com/paritytech/parity/pull/7807)) -- Move js & js-old code to github.com/parity-js ([#7685](https://github.com/paritytech/parity/pull/7685)) -- More changelogs :) ([#7782](https://github.com/paritytech/parity/pull/7782)) -- Actualized API set in help ([#7790](https://github.com/paritytech/parity/pull/7790)) -- Removed obsolete file ([#7788](https://github.com/paritytech/parity/pull/7788)) -- Update ropsten bootnodes ([#7776](https://github.com/paritytech/parity/pull/7776)) -- CHANGELOG for 1.9.1 and 1.8.8 ([#7775](https://github.com/paritytech/parity/pull/7775)) -- Enable byzantium features on non-ethash chains ([#7753](https://github.com/paritytech/parity/pull/7753)) -- Fix client not being dropped on shutdown ([#7695](https://github.com/paritytech/parity/pull/7695)) -- Filter-out nodes.json ([#7716](https://github.com/paritytech/parity/pull/7716)) -- Removes redundant parentheses ([#7721](https://github.com/paritytech/parity/pull/7721)) -- Transaction-pool fixes ([#7741](https://github.com/paritytech/parity/pull/7741)) -- More visible download link in README.md ([#7707](https://github.com/paritytech/parity/pull/7707)) -- Changelog for 1.9.0 ([#7664](https://github.com/paritytech/parity/pull/7664)) -- Add scroll when too many accounts ([#7677](https://github.com/paritytech/parity/pull/7677)) -- SecretStore: return HTTP 403 (access denied) if consensus is unreachable ([#7656](https://github.com/paritytech/parity/pull/7656)) -- Moved StopGaurd to it's own crate ([#7635](https://github.com/paritytech/parity/pull/7635)) +- Backports ([#8558](https://github.com/paritytech/parity/pull/8558)) + - Fetching logs by hash in blockchain database ([#8463](https://github.com/paritytech/parity/pull/8463)) + - Fetch logs by hash in blockchain database + - Fix tests + - Add unit test for branch block logs fetching + - Add docs that blocks must already be sorted + - Handle branch block cases properly + - typo: empty -> is_empty + - Remove return_empty_if_none by using a closure + - Use BTreeSet to avoid sorting again + - Move is_canon to BlockChain + - typo: pass value by reference + - Use loop and wrap inside blocks to simplify the code + - typo: missed a comment + - Pass on storage keys tracing to handle the case when it is not modified ([#8491](https://github.com/paritytech/parity/pull/8491)) + - Pass on storage keys even if it is not modified + - typo: account and storage query + - Fix tests + - Use state query directly because of suicided accounts + - Fix a RefCell borrow issue + - Add tests for unmodified storage trace + - Address grumbles + - typo: remove unwanted empty line + - ensure_cached compiles with the original signature + - Update wasmi and pwasm-utils ([#8493](https://github.com/paritytech/parity/pull/8493)) + - Update wasmi to 0.2 + - Update pwasm-utils to 0.1.5 + - Show imported messages for light client ([#8517](https://github.com/paritytech/parity/pull/8517)) + - Enable WebAssembly and Byzantium for Ellaism ([#8520](https://github.com/paritytech/parity/pull/8520)) + - Enable WebAssembly and Byzantium for Ellaism + - Fix indentation + - Remove empty lines + - Don't panic in import_block if invalid rlp ([#8522](https://github.com/paritytech/parity/pull/8522)) + - Don't panic in import_block if invalid rlp + - Remove redundant type annotation + - Replace RLP header view usage with safe decoding + - Node table sorting according to last contact data ([#8541](https://github.com/paritytech/parity/pull/8541)) + - network-devp2p: sort nodes in node table using last contact data + - network-devp2p: rename node contact types in node table json output + - network-devp2p: fix node table tests + - network-devp2p: note node failure when failed to establish connection + - network-devp2p: handle UselessPeer error + - network-devp2p: note failure when marking node as useless +- Betalize 1.11 :) ([#8475](https://github.com/paritytech/parity/pull/8475)) + - Betalize 1.11 :) + - Update Gitlab scripts + - Use master as gitlab latest + - Fix snap builds ([#8483](https://github.com/paritytech/parity/pull/8483)) + - Update hardcodedSync for Ethereum, Kovan, and Ropsten ([#8489](https://github.com/paritytech/parity/pull/8489)) +- Fix typos in vm description comment ([#8446](https://github.com/paritytech/parity/pull/8446)) +- Add changelog for 1.9.7 and 1.10.2 ([#8460](https://github.com/paritytech/parity/pull/8460)) +- Fix docker build ([#8462](https://github.com/paritytech/parity/pull/8462)) +- Parityshell::open `Return result` ([#8377](https://github.com/paritytech/parity/pull/8377)) +- Return error in case eth_call returns VM errors ([#8448](https://github.com/paritytech/parity/pull/8448)) +- Update wasmi ([#8452](https://github.com/paritytech/parity/pull/8452)) +- Allow 32 bit pipelines to fail ([#8454](https://github.com/paritytech/parity/pull/8454)) +- Update Cargo hidapi-rs dependency ([#8447](https://github.com/paritytech/parity/pull/8447)) +- Private transactions processing error handling ([#8431](https://github.com/paritytech/parity/pull/8431)) +- Improve VM executor stack size estimation rules ([#8439](https://github.com/paritytech/parity/pull/8439)) +- Block reward contract ([#8419](https://github.com/paritytech/parity/pull/8419)) +- Permission fix ([#8441](https://github.com/paritytech/parity/pull/8441)) +- Use forked app_dirs crate for reverted Windows dir behavior ([#8438](https://github.com/paritytech/parity/pull/8438)) +- Remove From::from. ([#8390](https://github.com/paritytech/parity/pull/8390)) +- Move ethcore::Error to error_chain ([#8386](https://github.com/paritytech/parity/pull/8386)) +- Changelogs for 1.9.6 and 1.10.1 ([#8411](https://github.com/paritytech/parity/pull/8411)) +- Fix receipts stripping. ([#8414](https://github.com/paritytech/parity/pull/8414)) +- Typo, docs parity_chainId: empty string -> None ([#8434](https://github.com/paritytech/parity/pull/8434)) +- Update zip to 0.3 ([#8381](https://github.com/paritytech/parity/pull/8381)) +- Fix TODO comments ([#8413](https://github.com/paritytech/parity/pull/8413)) +- Replace legacy Rlp with UntrustedRlp and use in ethcore rlp views ([#8316](https://github.com/paritytech/parity/pull/8316)) +- Tokio-core v0.1.16 -> v0.1.17 ([#8408](https://github.com/paritytech/parity/pull/8408)) +- More code refactoring to integrate Duration ([#8322](https://github.com/paritytech/parity/pull/8322)) +- Remove Tendermint extra_info due to seal inconsistencies ([#8367](https://github.com/paritytech/parity/pull/8367)) +- Use tokio::spawn in secret_store listener and fix Uri ([#8373](https://github.com/paritytech/parity/pull/8373)) +- Unify and limit rocksdb dependency places ([#8371](https://github.com/paritytech/parity/pull/8371)) +- Clarify that windows need perl and yasm ([#8402](https://github.com/paritytech/parity/pull/8402)) +- New Transaction Queue implementation ([#8074](https://github.com/paritytech/parity/pull/8074)) +- Some tweaks to main.rs for parity as a library ([#8370](https://github.com/paritytech/parity/pull/8370)) +- Handle queue import errors a bit more gracefully ([#8385](https://github.com/paritytech/parity/pull/8385)) +- Ci: fix change detection in master builds ([#8382](https://github.com/paritytech/parity/pull/8382)) +- Fix config test by adding no-hardcodec-sync ([#8380](https://github.com/paritytech/parity/pull/8380)) +- Fixed unsafe shell call on windows ([#8372](https://github.com/paritytech/parity/pull/8372)) +- Parity uses winapi 0.3.4 ([#8366](https://github.com/paritytech/parity/pull/8366)) +- No hardcoded client name ([#8368](https://github.com/paritytech/parity/pull/8368)) +- Add `util/mem` to zero out memory on drop. ([#8356](https://github.com/paritytech/parity/pull/8356)) +- Use atty instead of isatty ([#8365](https://github.com/paritytech/parity/pull/8365)) +- Increase gasLimit to 8'000'000 ([#8362](https://github.com/paritytech/parity/pull/8362)) +- Util `fake-fetch` ([#8363](https://github.com/paritytech/parity/pull/8363)) +- Bump snappy and ring, use single rayon version, closes [#8296](https://github.com/paritytech/parity/issues/8296) ([#8364](https://github.com/paritytech/parity/pull/8364)) +- Use async hyper server in secret_store and upgrade igd ([#8359](https://github.com/paritytech/parity/pull/8359)) +- Enable UI by default, but only display deprecation notice ([#8262](https://github.com/paritytech/parity/pull/8262)) +- Ethcrypto renamed to ethcore-crypto and moved to ethcore dir ([#8340](https://github.com/paritytech/parity/pull/8340)) +- Use hyper 0.11 in ethcore-miner and improvements in parity-reactor ([#8335](https://github.com/paritytech/parity/pull/8335)) +- Ethcore-sync ([#8347](https://github.com/paritytech/parity/pull/8347)) +- Rpc, eth_filter: return error if the filter id does not exist ([#8341](https://github.com/paritytech/parity/pull/8341)) +- Ethcore-stratum crate moved to ethcore directory ([#8338](https://github.com/paritytech/parity/pull/8338)) +- Secretstore: get rid of engine.signer dependency ([#8173](https://github.com/paritytech/parity/pull/8173)) +- Whisper cli ([#8201](https://github.com/paritytech/parity/pull/8201)) +- Replace_home for password_files, reserved_peers and log_file ([#8324](https://github.com/paritytech/parity/pull/8324)) +- Add Ethereum Social support ([#8325](https://github.com/paritytech/parity/pull/8325)) +- Private transactions integration pr ([#6422](https://github.com/paritytech/parity/pull/6422)) +- Decouple rocksdb dependency from ethcore ([#8320](https://github.com/paritytech/parity/pull/8320)) +- Remove the clone operation of code_cache ([#8334](https://github.com/paritytech/parity/pull/8334)) +- Fix the JSONRPC API not running with the light client ([#8326](https://github.com/paritytech/parity/pull/8326)) +- Read registry_address from block with REQUEST_CONFIRMATIONS_REQUIRED ([#8309](https://github.com/paritytech/parity/pull/8309)) +- Tweaks and add a Dockerfile for Android ([#8036](https://github.com/paritytech/parity/pull/8036)) +- Use associated type M::Error instead of Error ([#8308](https://github.com/paritytech/parity/pull/8308)) +- Remove InvalidParentHash in favor of assert! ([#8300](https://github.com/paritytech/parity/pull/8300)) +- Bump proc macro deps ([#8310](https://github.com/paritytech/parity/pull/8310)) +- Decouple timestamp open-block-assignment/verification to Engine ([#8305](https://github.com/paritytech/parity/pull/8305)) +- Validate if gas limit is not zero ([#8307](https://github.com/paritytech/parity/pull/8307)) +- Implement Easthub chain spec ([#8295](https://github.com/paritytech/parity/pull/8295)) +- Update some dependencies ([#8285](https://github.com/paritytech/parity/pull/8285)) +- Ethcore now uses Rayon 1.0 as a dependency ([#8296](https://github.com/paritytech/parity/pull/8296)) ([#8304](https://github.com/paritytech/parity/pull/8304)) +- Upgrader `remove raw unwrap` and bump semver ([#8251](https://github.com/paritytech/parity/pull/8251)) +- Cleaner binary shutdown system ([#8284](https://github.com/paritytech/parity/pull/8284)) +- Ethcore now uses rayon to 0.9 as a dependency ([#8296](https://github.com/paritytech/parity/pull/8296)) ([#8302](https://github.com/paritytech/parity/pull/8302)) +- Include suicided accounts in state diff ([#8297](https://github.com/paritytech/parity/pull/8297)) +- Remove evmjit ([#8229](https://github.com/paritytech/parity/pull/8229)) +- Build: fix updater rand dependency in Cargo.lock ([#8298](https://github.com/paritytech/parity/pull/8298)) +- Honor --max-peers if --min-peers is not specified ([#8087](https://github.com/paritytech/parity/pull/8087)) +- Auto-updater improvements ([#8078](https://github.com/paritytech/parity/pull/8078)) +- Dapps-fetcher: calculate keccak in-flight while reading the response ([#8294](https://github.com/paritytech/parity/pull/8294)) +- Cleanup Ellaism bootnodes ([#8276](https://github.com/paritytech/parity/pull/8276)) +- Allow unsafe js eval on Parity Wallet. ([#8204](https://github.com/paritytech/parity/pull/8204)) +- Remove RefCell from Header ([#8227](https://github.com/paritytech/parity/pull/8227)) +- Typo fix: todo with no content ([#8292](https://github.com/paritytech/parity/pull/8292)) +- Revert "ci: disable link-dead-code in coverage build ([#8118](https://github.com/paritytech/parity/pull/8118))" ([#8287](https://github.com/paritytech/parity/pull/8287)) +- Bump ethabi & ethereum-types. ([#8258](https://github.com/paritytech/parity/pull/8258)) +- Allow customization of max WS connections. ([#8257](https://github.com/paritytech/parity/pull/8257)) +- Supress TemporaryInvalid verification failures. ([#8256](https://github.com/paritytech/parity/pull/8256)) +- Return null number for pending block in eth_getBlockByNumber ([#8281](https://github.com/paritytech/parity/pull/8281)) +- Use constant durations ([#8278](https://github.com/paritytech/parity/pull/8278)) +- Typo fix: Mode doc - RLP should be client ([#8283](https://github.com/paritytech/parity/pull/8283)) +- Eth_uninstallfilter should return false for non-existent filter ([#8280](https://github.com/paritytech/parity/pull/8280)) +- Update `app_dirs` to 1.2.1 ([#8268](https://github.com/paritytech/parity/pull/8268)) +- Add missing license header for runtime.rs ([#8252](https://github.com/paritytech/parity/pull/8252)) +- Warp-only sync with warp-barrier [blocknumber] flag. ([#8228](https://github.com/paritytech/parity/pull/8228)) +- Replace all Rlp usages with UntrustedRlp except for ethcore views ([#8233](https://github.com/paritytech/parity/pull/8233)) +- Add test for ethstore-cli, fixes [#8027](https://github.com/paritytech/parity/issues/8027) ([#8187](https://github.com/paritytech/parity/pull/8187)) +- Update musicoin spec in line with gmc v2.6.2 ([#8242](https://github.com/paritytech/parity/pull/8242)) +- Fixed ethcore tx_filter ([#8200](https://github.com/paritytech/parity/pull/8200)) +- Update CLI help for jsonrpc-apis, ws-apis and ipc-apis ([#8234](https://github.com/paritytech/parity/pull/8234)) +- Remove network stats ([#8225](https://github.com/paritytech/parity/pull/8225)) +- Node-filter does not use ChainNotify ([#8231](https://github.com/paritytech/parity/pull/8231)) +- Implement hardcoded sync in the light client ([#8075](https://github.com/paritytech/parity/pull/8075)) +- Update some of the dependencies for WASM ([#8223](https://github.com/paritytech/parity/pull/8223)) +- Bump wasmi version ([#8209](https://github.com/paritytech/parity/pull/8209)) +- Updated jsonrpc to point to the 1.11 branch ([#8180](https://github.com/paritytech/parity/pull/8180)) +- Change name Wallet -> UI ([#8164](https://github.com/paritytech/parity/pull/8164)) +- Introduce Parity UI ([#8202](https://github.com/paritytech/parity/pull/8202)) +- Update Changelogs ([#8175](https://github.com/paritytech/parity/pull/8175)) +- Returns number of topcis to take fr.. ([#8199](https://github.com/paritytech/parity/pull/8199)) +- Make docopt usage non-const ([#8189](https://github.com/paritytech/parity/pull/8189)) +- Avoid allocations when computing triehash. ([#8176](https://github.com/paritytech/parity/pull/8176)) +- Handle rlp decoding Result in patricia trie ([#8166](https://github.com/paritytech/parity/pull/8166)) +- Bump wasm libs ([#8171](https://github.com/paritytech/parity/pull/8171)) +- Re-enable signer, even with no UI. ([#8167](https://github.com/paritytech/parity/pull/8167)) +- Update daemonize ([#8165](https://github.com/paritytech/parity/pull/8165)) +- Some tiny modifications. ([#8163](https://github.com/paritytech/parity/pull/8163)) +- Secretstore: store key author address in db ([#7887](https://github.com/paritytech/parity/pull/7887)) +- Rename DatabaseValueView::new to from_rlp ([#8159](https://github.com/paritytech/parity/pull/8159)) +- Dapps: update parity-ui dependencies ([#8160](https://github.com/paritytech/parity/pull/8160)) +- Disable UI by default. ([#8105](https://github.com/paritytech/parity/pull/8105)) +- Fix wasmi x32 builds ([#8155](https://github.com/paritytech/parity/pull/8155)) +- Postpone Kovan hard fork ([#8137](https://github.com/paritytech/parity/pull/8137)) +- Secretstore: ability to identify requester via Public/Address ([#7886](https://github.com/paritytech/parity/pull/7886)) +- Optional dependency on secp256k1 for ethcrypto ([#8109](https://github.com/paritytech/parity/pull/8109)) +- Network: init discovery using healthy nodes ([#8061](https://github.com/paritytech/parity/pull/8061)) +- Check one step deeper if we're on release track branches ([#8134](https://github.com/paritytech/parity/pull/8134)) +- Explicitly mention pruning_history uses RAM ([#8130](https://github.com/paritytech/parity/pull/8130)) +- Remove `ethcrypto::{en,de}crypt_single_message`. ([#8126](https://github.com/paritytech/parity/pull/8126)) +- Fix typo ([#8124](https://github.com/paritytech/parity/pull/8124)) +- Secret_store: use `ecies::encrypt`/`ecies::decrypt`. ([#8125](https://github.com/paritytech/parity/pull/8125)) +- Fix comment for fn gas() in wasm/runtime ([#8122](https://github.com/paritytech/parity/pull/8122)) +- Structured rlp encoding in journaldb ([#8047](https://github.com/paritytech/parity/pull/8047)) +- Ci: disable link-dead-code in coverage build ([#8118](https://github.com/paritytech/parity/pull/8118)) +- Fix trace filter returning returning unrelated reward calls, closes [#8070](https://github.com/paritytech/parity/issues/8070) ([#8098](https://github.com/paritytech/parity/pull/8098)) +- Const time comparison ([#8113](https://github.com/paritytech/parity/pull/8113)) +- Replace reqwest with hyper ([#8099](https://github.com/paritytech/parity/pull/8099)) +- More dos protection ([#8104](https://github.com/paritytech/parity/pull/8104)) +- Remove the time dependency where possible ([#8100](https://github.com/paritytech/parity/pull/8100)) +- Fix comment for gas extern in Wasm runtime ([#8101](https://github.com/paritytech/parity/pull/8101)) +- Replace std::env::temp_dir with tempdir in tests ([#8103](https://github.com/paritytech/parity/pull/8103)) +- Fix Cargo.lock not parsable ([#8102](https://github.com/paritytech/parity/pull/8102)) +- Additional data in EVMTestClient ([#7964](https://github.com/paritytech/parity/pull/7964)) +- Update serde, serde-derive, ethabi-derive, syn, quote and rlp_derive ([#8085](https://github.com/paritytech/parity/pull/8085)) +- Ethcore-service ([#8089](https://github.com/paritytech/parity/pull/8089)) +- [contract-client] refactor ([#7978](https://github.com/paritytech/parity/pull/7978)) +- Revert removing blooms ([#8066](https://github.com/paritytech/parity/pull/8066)) +- Ethcore test::helpers cleanup ([#8086](https://github.com/paritytech/parity/pull/8086)) +- Add some dos protection ([#8084](https://github.com/paritytech/parity/pull/8084)) +- Wasm libraries bump ([#7970](https://github.com/paritytech/parity/pull/7970)) +- Echo back the message hash of a ping in the pong request ([#8042](https://github.com/paritytech/parity/pull/8042)) +- Add Kovan WASM activation blocknumber ([#8057](https://github.com/paritytech/parity/pull/8057)) +- [ethkey] Unify debug/display for Address/Public/Secret ([#8076](https://github.com/paritytech/parity/pull/8076)) +- Limit incoming connections. ([#8060](https://github.com/paritytech/parity/pull/8060)) +- Max code size on Kovan ([#8067](https://github.com/paritytech/parity/pull/8067)) +- Updater: apply exponential backoff after download failure ([#8059](https://github.com/paritytech/parity/pull/8059)) +- Make blockchain functions more idiomatic, avoid needless writes to cache_man ([#8054](https://github.com/paritytech/parity/pull/8054)) +- Make patricia-trie more idiomatic and remove redundant code ([#8056](https://github.com/paritytech/parity/pull/8056)) +- Abstract devp2p ([#8048](https://github.com/paritytech/parity/pull/8048)) +- Update refs to shell ([#8051](https://github.com/paritytech/parity/pull/8051)) +- Fix cache & snapcraft CI build ([#8052](https://github.com/paritytech/parity/pull/8052)) +- Prelude to the block module cleanup ([#8025](https://github.com/paritytech/parity/pull/8025)) +- Add MCIP-6 Byzyantium transition to Musicoin spec ([#7841](https://github.com/paritytech/parity/pull/7841)) +- Bump master to 1.11.0 ([#8021](https://github.com/paritytech/parity/pull/8021)) +- `client` refactoring ([#7038](https://github.com/paritytech/parity/pull/7038)) +- [hardware wallet] sleeping -> pollling ([#8018](https://github.com/paritytech/parity/pull/8018)) +- Fixed broken link in README ([#8012](https://github.com/paritytech/parity/pull/8012)) +- Support parity protocol. ([#8035](https://github.com/paritytech/parity/pull/8035)) +- Add changelog for 1.8.11 stable and 1.9.4 beta ([#8017](https://github.com/paritytech/parity/pull/8017)) +- Fix for verify_block_basic crashing on invalid transaction rlp ([#8032](https://github.com/paritytech/parity/pull/8032)) +- Extract the hard dependency on rocksdb from the light client ([#8034](https://github.com/paritytech/parity/pull/8034)) +- Fixed parsing ethash seals and verify_block_undordered ([#8031](https://github.com/paritytech/parity/pull/8031)) +- Fixed ethstore sign ([#8026](https://github.com/paritytech/parity/pull/8026)) +- Ci: Fix cargo cache ([#7968](https://github.com/paritytech/parity/pull/7968)) +- Update ref to new shell ([#8024](https://github.com/paritytech/parity/pull/8024)) ## Previous releases -- [CHANGELOG-1.9](docs/CHANGELOG-1.9.md) (_stable_) +- [CHANGELOG-1.10](docs/CHANGELOG-1.10.md) (_stable_) +- [CHANGELOG-1.9](docs/CHANGELOG-1.9.md) (EOL: 2018-05-09) - [CHANGELOG-1.8](docs/CHANGELOG-1.8.md) (EOL: 2018-03-22) - [CHANGELOG-1.7](docs/CHANGELOG-1.7.md) (EOL: 2018-01-25) - [CHANGELOG-1.6](docs/CHANGELOG-1.6.md) (EOL: 2017-10-15) diff --git a/Cargo.lock b/Cargo.lock index 211fbbd61cd8a4aed6501418cb087750ff245c9c..b1b156dbaff7c75fab819d7f3fad39e40ae9e640 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,3 +1,8 @@ +[[package]] +name = "adler32" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "aho-corasick" version = "0.6.4" @@ -13,11 +18,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "app_dirs" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" +version = "1.2.1" +source = "git+https://github.com/paritytech/app-dirs-rs#0b37f9481ce29e9d5174ad185bca695b206368eb" dependencies = [ "ole32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "shell32-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "shell32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "xdg 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -30,6 +35,11 @@ dependencies = [ "nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "assert_matches" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "aster" version = "0.41.0" @@ -40,12 +50,12 @@ dependencies = [ [[package]] name = "atty" -version = "0.2.2" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -65,7 +75,7 @@ name = "backtrace-sys" version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -97,24 +107,14 @@ dependencies = [ "safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "bigint" -version = "4.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "bincode" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -154,7 +154,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "bloomchain" version = "0.2.0" dependencies = [ - "ethbloom 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethbloom 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -168,6 +168,11 @@ dependencies = [ "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "build_const" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "byteorder" version = "1.2.1" @@ -184,10 +189,10 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.4" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "rayon 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -204,6 +209,16 @@ dependencies = [ "serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "chrono" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-integer 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "cid" version = "0.2.3" @@ -220,7 +235,7 @@ version = "2.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", - "atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "atty 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -228,24 +243,15 @@ dependencies = [ "vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "coco" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "either 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "scopeguard 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "common-types" version = "0.1.0" dependencies = [ "ethcore-bytes 0.1.0", - "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "ethjson 0.1.0", "heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hash 0.1.0", + "keccak-hash 0.1.2", "rlp 0.2.1", "rlp_derive 0.1.0", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -260,12 +266,11 @@ dependencies = [ ] [[package]] -name = "cookie" -version = "0.3.1" +name = "crc" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -273,6 +278,67 @@ name = "crossbeam" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "crossbeam-deque" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-deque" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-epoch 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-utils" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-utils" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "crunchy" version = "0.1.6" @@ -318,8 +384,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "dir" version = "0.1.0" dependencies = [ - "app_dirs 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "app_dirs 1.2.1 (git+https://github.com/paritytech/app-dirs-rs)", + "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "journaldb 0.1.0", ] @@ -330,8 +396,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", "strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -381,47 +447,47 @@ version = "0.5.7" source = "git+https://github.com/paritytech/rust-secp256k1#db81cfea59014b4d176f10f86ed52e1a130b6822" dependencies = [ "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "ethabi" -version = "5.1.0" +version = "5.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-keccak 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "ethabi-contract" -version = "5.0.3" +version = "5.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "ethabi-derive" -version = "5.0.8" +version = "5.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "ethabi 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ethabi 5.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "heck 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.12.14 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "ethash" -version = "1.11.0" +version = "1.12.0" dependencies = [ "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "either 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hash 0.1.0", + "keccak-hash 0.1.2", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -431,19 +497,19 @@ dependencies = [ [[package]] name = "ethbloom" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "ethereum-types-serialize 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "fixed-hash 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-keccak 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "fixed-hash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "ethcore" -version = "1.11.0" +version = "1.12.0" dependencies = [ "ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", "bloomchain 0.2.0", @@ -451,29 +517,31 @@ dependencies = [ "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "common-types 0.1.0", "crossbeam 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi-contract 5.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi-derive 5.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "ethash 1.11.0", + "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ethabi 5.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ethabi-contract 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ethabi-derive 5.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethash 1.12.0", "ethcore-bloom-journal 0.1.0", "ethcore-bytes 0.1.0", - "ethcore-io 1.11.0", - "ethcore-logger 1.11.0", - "ethcore-miner 1.11.0", - "ethcore-stratum 1.11.0", + "ethcore-crypto 0.1.0", + "ethcore-io 1.12.0", + "ethcore-logger 1.12.0", + "ethcore-miner 1.12.0", + "ethcore-stratum 1.12.0", "ethcore-transaction 0.1.0", - "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "ethjson 0.1.0", "ethkey 0.3.0", "ethstore 0.2.0", "evm 0.1.0", - "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "hardware-wallet 1.11.0", + "fetch 0.1.0", + "hardware-wallet 1.12.0", "hashdb 0.1.1", "heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", "journaldb 0.1.0", - "keccak-hash 0.1.0", + "keccak-hash 0.1.2", "kvdb 0.1.0", "kvdb-memorydb 0.1.0", "kvdb-rocksdb 0.1.0", @@ -483,24 +551,20 @@ dependencies = [ "macros 0.1.0", "memory-cache 0.1.0", "memorydb 0.1.1", - "migration 0.1.0", "num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-machine 0.1.0", "parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "patricia-trie 0.1.0", - "price-info 1.11.0", "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rayon 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "rlp 0.2.1", "rlp_compress 0.1.0", "rlp_derive 0.1.0", - "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "snappy 0.1.0 (git+https://github.com/paritytech/rust-snappy)", "stats 0.1.0", "stop-guard 0.1.0", - "table 0.1.0", "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "trace-time 0.1.0", "trie-standardmap 0.1.0", @@ -523,37 +587,53 @@ dependencies = [ name = "ethcore-bytes" version = "0.1.0" +[[package]] +name = "ethcore-crypto" +version = "0.1.0" +dependencies = [ + "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "quick-error 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ring 0.12.1 (git+https://github.com/paritytech/ring)", + "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "ethcore-devtools" -version = "1.11.0" +version = "1.12.0" [[package]] name = "ethcore-io" -version = "1.11.0" +version = "1.12.0" dependencies = [ "crossbeam 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)", + "timer 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "ethcore-light" -version = "1.11.0" +version = "1.12.0" dependencies = [ "bincode 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore 1.11.0", + "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ethcore 1.12.0", "ethcore-bytes 0.1.0", - "ethcore-io 1.11.0", - "ethcore-network 1.11.0", + "ethcore-io 1.12.0", + "ethcore-network 1.12.0", "ethcore-transaction 0.1.0", - "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", + "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", "hashdb 0.1.1", "heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hash 0.1.0", + "keccak-hash 0.1.2", "kvdb 0.1.0", "kvdb-memorydb 0.1.0", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", @@ -565,8 +645,8 @@ dependencies = [ "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "rlp 0.2.1", "rlp_derive 0.1.0", - "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "stats 0.1.0", "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -576,12 +656,12 @@ dependencies = [ [[package]] name = "ethcore-logger" -version = "1.11.0" +version = "1.12.0" dependencies = [ "ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "atty 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "isatty 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -589,68 +669,69 @@ dependencies = [ "time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "ethcore-migrations" -version = "0.1.0" -dependencies = [ - "migration 0.1.0", -] - [[package]] name = "ethcore-miner" -version = "1.11.0" +version = "1.12.0" dependencies = [ - "common-types 0.1.0", - "ethabi 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi-contract 5.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi-derive 5.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "ethash 1.11.0", + "ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ethash 1.12.0", "ethcore-transaction 0.1.0", - "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "ethkey 0.3.0", - "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", + "fetch 0.1.0", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.10.0-a.0 (git+https://github.com/paritytech/hyper)", - "keccak-hash 0.1.0", + "hyper 0.11.24 (registry+https://github.com/rust-lang/crates.io-index)", + "keccak-hash 0.1.2", "linked-hash-map 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-reactor 0.1.0", "parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", + "price-info 1.12.0", + "rlp 0.2.1", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "table 0.1.0", - "transient-hashmap 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "trace-time 0.1.0", + "transaction-pool 1.12.1", + "url 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "ethcore-network" -version = "1.11.0" +version = "1.12.0" dependencies = [ + "assert_matches 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore-io 1.11.0", - "ethcrypto 0.1.0", - "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethcore-crypto 0.1.0", + "ethcore-io 1.12.0", + "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "ethkey 0.3.0", "ipnetwork 0.12.7 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", "rlp 0.2.1", "snappy 0.1.0 (git+https://github.com/paritytech/rust-snappy)", ] [[package]] name = "ethcore-network-devp2p" -version = "1.11.0" +version = "1.12.0" dependencies = [ "ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", + "assert_matches 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "ethcore-bytes 0.1.0", - "ethcore-io 1.11.0", - "ethcore-logger 1.11.0", - "ethcore-network 1.11.0", - "ethcrypto 0.1.0", - "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethcore-crypto 0.1.0", + "ethcore-io 1.12.0", + "ethcore-logger 1.12.0", + "ethcore-network 1.12.0", + "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "ethkey 0.3.0", - "igd 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "igd 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "ipnetwork 0.12.7 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hash 0.1.0", + "keccak-hash 0.1.2", "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", @@ -660,13 +741,48 @@ dependencies = [ "rlp 0.2.1", "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "snappy 0.1.0 (git+https://github.com/paritytech/rust-snappy)", "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-keccak 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ethcore-private-tx" +version = "1.0.0" +dependencies = [ + "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ethabi 5.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ethabi-contract 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ethabi-derive 5.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethcore 1.12.0", + "ethcore-bytes 0.1.0", + "ethcore-crypto 0.1.0", + "ethcore-io 1.12.0", + "ethcore-logger 1.12.0", + "ethcore-miner 1.12.0", + "ethcore-transaction 0.1.0", + "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethjson 0.1.0", + "ethkey 0.3.0", + "fetch 0.1.0", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "keccak-hash 0.1.2", + "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", + "patricia-trie 0.1.0", + "rand 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)", + "rlp 0.2.1", + "rlp_derive 0.1.0", + "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -674,33 +790,35 @@ name = "ethcore-secretstore" version = "1.0.0" dependencies = [ "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi-contract 5.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi-derive 5.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore 1.11.0", + "ethabi 5.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ethabi-contract 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ethabi-derive 5.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethcore 1.12.0", "ethcore-bytes 0.1.0", - "ethcore-logger 1.11.0", - "ethcrypto 0.1.0", - "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethcore-crypto 0.1.0", + "ethcore-logger 1.12.0", + "ethcore-sync 1.12.0", + "ethcore-transaction 0.1.0", + "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "ethkey 0.3.0", - "ethsync 1.11.0", - "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hash 0.1.0", + "hyper 0.11.24 (registry+https://github.com/rust-lang/crates.io-index)", + "keccak-hash 0.1.2", "kvdb 0.1.0", "kvdb-rocksdb 0.1.0", "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-keccak 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -711,71 +829,96 @@ name = "ethcore-service" version = "0.1.0" dependencies = [ "ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore 1.11.0", - "ethcore-io 1.11.0", + "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ethcore 1.12.0", + "ethcore-io 1.12.0", + "ethcore-private-tx 1.0.0", + "ethcore-sync 1.12.0", "kvdb 0.1.0", "kvdb-rocksdb 0.1.0", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "stop-guard 0.1.0", "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "trace-time 0.1.0", ] [[package]] name = "ethcore-stratum" -version = "1.11.0" +version = "1.12.0" dependencies = [ "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore-logger 1.11.0", - "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethcore-logger 1.12.0", + "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 8.0.1 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11)", "jsonrpc-macros 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11)", "jsonrpc-tcp-server 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11)", - "keccak-hash 0.1.0", + "keccak-hash 0.1.2", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "ethcore-transaction" -version = "0.1.0" +name = "ethcore-sync" +version = "1.12.0" dependencies = [ - "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethjson 0.1.0", + "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "ethcore 1.12.0", + "ethcore-bytes 0.1.0", + "ethcore-io 1.12.0", + "ethcore-light 1.12.0", + "ethcore-network 1.12.0", + "ethcore-network-devp2p 1.12.0", + "ethcore-private-tx 1.0.0", + "ethcore-transaction 0.1.0", + "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "ethkey 0.3.0", - "evm 0.1.0", "heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hash 0.1.0", + "ipnetwork 0.12.7 (registry+https://github.com/rust-lang/crates.io-index)", + "keccak-hash 0.1.2", + "kvdb 0.1.0", + "kvdb-memorydb 0.1.0", + "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "macros 0.1.0", + "parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", + "plain_hasher 0.1.0", + "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "rlp 0.2.1", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unexpected 0.1.0", + "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "trace-time 0.1.0", + "triehash 0.1.0", ] [[package]] -name = "ethcrypto" +name = "ethcore-transaction" version = "0.1.0" dependencies = [ - "eth-secp256k1 0.5.7 (git+https://github.com/paritytech/rust-secp256k1)", - "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethjson 0.1.0", "ethkey 0.3.0", - "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-keccak 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "evm 0.1.0", + "heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "keccak-hash 0.1.2", + "rlp 0.2.1", + "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unexpected 0.1.0", ] [[package]] name = "ethereum-types" -version = "0.2.2" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "ethbloom 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethbloom 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "ethereum-types-serialize 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "fixed-hash 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", - "uint 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "fixed-hash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "uint 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -783,17 +926,17 @@ name = "ethereum-types-serialize" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "ethjson" version = "0.1.0" dependencies = [ - "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -804,14 +947,16 @@ dependencies = [ "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "edit-distance 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "eth-secp256k1 0.5.7 (git+https://github.com/paritytech/rust-secp256k1)", - "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethcore-crypto 0.1.0", + "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "mem 0.1.0", "parity-wordlist 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "quick-error 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-keccak 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -824,8 +969,8 @@ dependencies = [ "panic_hook 0.1.0", "parity-wordlist 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", "threadpool 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -834,8 +979,8 @@ name = "ethstore" version = "0.2.0" dependencies = [ "dir 0.1.0", - "ethcrypto 0.1.0", - "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethcore-crypto 0.1.0", + "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "ethkey 0.3.0", "itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", @@ -844,16 +989,14 @@ dependencies = [ "parity-wordlist 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-keccak 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -863,42 +1006,13 @@ dependencies = [ "dir 0.1.0", "docopt 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)", "ethstore 0.2.0", - "num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "panic_hook 0.1.0", "parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "ethsync" -version = "1.11.0" -dependencies = [ - "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore 1.11.0", - "ethcore-bytes 0.1.0", - "ethcore-io 1.11.0", - "ethcore-light 1.11.0", - "ethcore-network 1.11.0", - "ethcore-network-devp2p 1.11.0", - "ethcore-transaction 0.1.0", - "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethkey 0.3.0", - "heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ipnetwork 0.12.7 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hash 0.1.0", - "kvdb 0.1.0", - "kvdb-memorydb 0.1.0", - "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "macros 0.1.0", - "parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", - "plain_hasher 0.1.0", - "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.2.1", - "semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "triehash 0.1.0", + "serde 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -906,10 +1020,9 @@ name = "evm" version = "0.1.0" dependencies = [ "bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "evmjit 1.11.0", + "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hash 0.1.0", + "keccak-hash 0.1.2", "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "memory-cache 0.1.0", @@ -923,26 +1036,28 @@ name = "evmbin" version = "0.1.0" dependencies = [ "docopt 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore 1.11.0", + "ethcore 1.12.0", "ethcore-bytes 0.1.0", "ethcore-transaction 0.1.0", - "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "ethjson 0.1.0", "evm 0.1.0", "panic_hook 0.1.0", "pretty_assertions 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "vm 0.1.0", ] [[package]] -name = "evmjit" -version = "1.11.0" +name = "fake-fetch" +version = "0.0.1" dependencies = [ - "tiny-keccak 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "fetch 0.1.0", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.11.24 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -957,18 +1072,19 @@ dependencies = [ name = "fetch" version = "0.1.0" dependencies = [ - "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", "futures-timer 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.11.24 (registry+https://github.com/rust-lang/crates.io-index)", "hyper-rustls 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "fixed-hash" -version = "0.1.3" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -977,13 +1093,18 @@ dependencies = [ "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "fixedbitset" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "flate2" -version = "0.2.20" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", - "miniz-sys 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "miniz_oxide_c_api 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1007,7 +1128,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "futures" -version = "0.1.18" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1015,8 +1136,8 @@ name = "futures-cpupool" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1024,16 +1145,13 @@ name = "futures-timer" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "gcc" version = "0.3.54" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rayon 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", -] [[package]] name = "getopts" @@ -1064,16 +1182,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "hardware-wallet" -version = "1.11.0" +version = "1.12.0" dependencies = [ - "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "ethkey 0.3.0", "hidapi 0.3.1 (git+https://github.com/paritytech/hidapi-rs)", "libusb 0.3.0 (git+https://github.com/paritytech/libusb-rs)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "protobuf 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "trezor-sys 1.0.0 (git+https://github.com/paritytech/trezor-sys)", ] @@ -1082,7 +1201,7 @@ name = "hashdb" version = "0.1.1" dependencies = [ "elastic-array 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1109,9 +1228,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "hidapi" version = "0.3.1" -source = "git+https://github.com/paritytech/hidapi-rs#e77ea09c98f29ea8855dd9cd9461125a28ca9125" +source = "git+https://github.com/paritytech/hidapi-rs#70ec4bd1b755ec5dd32ad2be0c8345864147c8bc" dependencies = [ - "cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1120,43 +1239,6 @@ name = "httparse" version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "hyper" -version = "0.10.0-a.0" -source = "git+https://github.com/paritytech/hyper#da10f69a4924cd44e98a8d1f9562bd7534d13dcc" -dependencies = [ - "cookie 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "httparse 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rotor 0.6.3 (git+https://github.com/tailhook/rotor)", - "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", - "spmc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)", - "unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "vecio 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "hyper" -version = "0.10.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "httparse 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)", - "traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "hyper" version = "0.11.24" @@ -1164,7 +1246,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "base64 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "httparse 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1174,8 +1256,8 @@ dependencies = [ "percent-encoding 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "relay 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "unicase 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1187,11 +1269,11 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "ct-logs 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.11.24 (registry+https://github.com/rust-lang/crates.io-index)", "rustls 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-rustls 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1210,14 +1292,18 @@ dependencies = [ [[package]] name = "igd" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.11.24 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "xml-rs 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "xmltree 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-retry 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-timer 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "xml-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "xmltree 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1244,16 +1330,6 @@ name = "ipnetwork" version = "0.12.7" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "isatty" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "itertools" version = "0.5.10" @@ -1272,11 +1348,11 @@ name = "journaldb" version = "0.1.0" dependencies = [ "ethcore-bytes 0.1.0", - "ethcore-logger 1.11.0", - "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethcore-logger 1.12.0", + "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "hashdb 0.1.1", "heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hash 0.1.0", + "keccak-hash 0.1.2", "kvdb 0.1.0", "kvdb-memorydb 0.1.0", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1290,19 +1366,19 @@ dependencies = [ [[package]] name = "jsonrpc-core" version = "8.0.1" -source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11#06bec6ab682ec4ce16d7e7daf2612870c4272028" +source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11#c8e6336798be4444953def351099078617d40efd" dependencies = [ - "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "jsonrpc-http-server" version = "8.0.0" -source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11#06bec6ab682ec4ce16d7e7daf2612870c4272028" +source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11#c8e6336798be4444953def351099078617d40efd" dependencies = [ "hyper 0.11.24 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 8.0.1 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11)", @@ -1315,7 +1391,7 @@ dependencies = [ [[package]] name = "jsonrpc-ipc-server" version = "8.0.0" -source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11#06bec6ab682ec4ce16d7e7daf2612870c4272028" +source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11#c8e6336798be4444953def351099078617d40efd" dependencies = [ "jsonrpc-core 8.0.1 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11)", "jsonrpc-server-utils 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11)", @@ -1327,17 +1403,17 @@ dependencies = [ [[package]] name = "jsonrpc-macros" version = "8.0.0" -source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11#06bec6ab682ec4ce16d7e7daf2612870c4272028" +source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11#c8e6336798be4444953def351099078617d40efd" dependencies = [ "jsonrpc-core 8.0.1 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11)", "jsonrpc-pubsub 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11)", - "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "jsonrpc-pubsub" version = "8.0.0" -source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11#06bec6ab682ec4ce16d7e7daf2612870c4272028" +source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11#c8e6336798be4444953def351099078617d40efd" dependencies = [ "jsonrpc-core 8.0.1 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1347,20 +1423,20 @@ dependencies = [ [[package]] name = "jsonrpc-server-utils" version = "8.0.0" -source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11#06bec6ab682ec4ce16d7e7daf2612870c4272028" +source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11#c8e6336798be4444953def351099078617d40efd" dependencies = [ "bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "globset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 8.0.1 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "jsonrpc-tcp-server" version = "8.0.0" -source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11#06bec6ab682ec4ce16d7e7daf2612870c4272028" +source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11#c8e6336798be4444953def351099078617d40efd" dependencies = [ "jsonrpc-core 8.0.1 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11)", "jsonrpc-server-utils 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11)", @@ -1372,7 +1448,7 @@ dependencies = [ [[package]] name = "jsonrpc-ws-server" version = "8.0.0" -source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11#06bec6ab682ec4ce16d7e7daf2612870c4272028" +source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11#c8e6336798be4444953def351099078617d40efd" dependencies = [ "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 8.0.1 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11)", @@ -1385,12 +1461,11 @@ dependencies = [ [[package]] name = "keccak-hash" -version = "0.1.0" +version = "0.1.2" dependencies = [ - "cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-keccak 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1424,11 +1499,11 @@ name = "kvdb-rocksdb" version = "0.1.0" dependencies = [ "elastic-array 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "interleaved-ordered 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "kvdb 0.1.0", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "rocksdb 0.4.5 (git+https://github.com/paritytech/rust-rocksdb)", @@ -1475,7 +1550,7 @@ name = "libusb-sys" version = "0.2.4" source = "git+https://github.com/paritytech/libusb-sys#14bdb698003731b6344a79e1d814704e44363e7c" dependencies = [ - "cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1532,6 +1607,10 @@ name = "matches" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "mem" +version = "0.1.0" + [[package]] name = "memchr" version = "2.0.1" @@ -1549,6 +1628,11 @@ dependencies = [ "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "memoffset" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "memory-cache" version = "0.1.0" @@ -1566,18 +1650,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "memorydb" version = "0.1.1" dependencies = [ - "bigint 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "elastic-array 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "hashdb 0.1.1", "heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hash 0.1.0", + "keccak-hash 0.1.2", "plain_hasher 0.1.0", "rlp 0.2.1", ] [[package]] -name = "migration" +name = "migration-rocksdb" version = "0.1.0" dependencies = [ "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1588,14 +1671,6 @@ dependencies = [ "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "mime" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "mime" version = "0.3.4" @@ -1616,12 +1691,23 @@ dependencies = [ ] [[package]] -name = "miniz-sys" -version = "0.1.10" +name = "miniz_oxide" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "adler32 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "miniz_oxide_c_api" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", + "crc 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "miniz_oxide 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1644,14 +1730,13 @@ dependencies = [ [[package]] name = "mio-named-pipes" -version = "0.1.4" -source = "git+https://github.com/alexcrichton/mio-named-pipes#9c1bbb985b74374d3b7eda76937279f8e977ef81" +version = "0.1.5" +source = "git+https://github.com/alexcrichton/mio-named-pipes#6ad80e67fe7993423b281bc13d307785ade05d37" dependencies = [ - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", - "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "miow 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1674,14 +1759,22 @@ dependencies = [ "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "miow" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "socket2 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "msdos_time" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1697,10 +1790,15 @@ name = "multihash" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "ring 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-keccak 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ring 0.12.1 (git+https://github.com/paritytech/ring)", + "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "nan-preserving-float" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "net2" version = "0.2.31" @@ -1714,17 +1812,17 @@ dependencies = [ ] [[package]] -name = "node-filter" -version = "1.11.0" -dependencies = [ - "ethabi 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi-contract 5.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi-derive 5.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore 1.11.0", - "ethcore-bytes 0.1.0", - "ethcore-io 1.11.0", - "ethcore-network-devp2p 1.11.0", - "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +name = "node-filter" +version = "1.12.0" +dependencies = [ + "ethabi 5.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ethabi-contract 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ethabi-derive 5.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethcore 1.12.0", + "ethcore-io 1.12.0", + "ethcore-network 1.12.0", + "ethcore-network-devp2p 1.12.0", + "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "kvdb-memorydb 0.1.0", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "lru-cache 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1736,14 +1834,14 @@ dependencies = [ name = "node-health" version = "0.1.0" dependencies = [ - "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "ntp 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "parity-reactor 0.1.0", "parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1783,7 +1881,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "num-integer 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1806,8 +1904,11 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.1.40" +version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-traits 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "num-traits" @@ -1816,7 +1917,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "num_cpus" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1827,7 +1928,7 @@ name = "number_prefix" version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "num-traits 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1849,10 +1950,15 @@ name = "ordered-float" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "num-traits 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", "unreachable 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "ordermap" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "owning_ref" version = "0.3.3" @@ -1870,57 +1976,57 @@ dependencies = [ [[package]] name = "parity" -version = "1.11.0" +version = "1.12.0" dependencies = [ "ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", - "app_dirs 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "atty 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.29.1 (registry+https://github.com/rust-lang/crates.io-index)", "ctrlc 1.1.1 (git+https://github.com/paritytech/rust-ctrlc.git)", "daemonize 0.2.3 (git+https://github.com/paritytech/daemonize)", "dir 0.1.0", "docopt 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore 1.11.0", + "ethcore 1.12.0", "ethcore-bytes 0.1.0", - "ethcore-io 1.11.0", - "ethcore-light 1.11.0", - "ethcore-logger 1.11.0", - "ethcore-migrations 0.1.0", - "ethcore-miner 1.11.0", - "ethcore-network 1.11.0", + "ethcore-io 1.12.0", + "ethcore-light 1.12.0", + "ethcore-logger 1.12.0", + "ethcore-miner 1.12.0", + "ethcore-network 1.12.0", + "ethcore-private-tx 1.0.0", "ethcore-secretstore 1.0.0", "ethcore-service 0.1.0", - "ethcore-stratum 1.11.0", + "ethcore-sync 1.12.0", "ethcore-transaction 0.1.0", - "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "ethkey 0.3.0", - "ethsync 1.11.0", + "fake-fetch 0.0.1", "fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "ipnetwork 0.12.7 (registry+https://github.com/rust-lang/crates.io-index)", - "isatty 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "journaldb 0.1.0", "jsonrpc-core 8.0.1 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11)", - "keccak-hash 0.1.0", + "keccak-hash 0.1.2", "kvdb 0.1.0", "kvdb-rocksdb 0.1.0", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "migration 0.1.0", - "node-filter 1.11.0", + "mem 0.1.0", + "migration-rocksdb 0.1.0", + "node-filter 1.12.0", "node-health 0.1.0", - "num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "number_prefix 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", "panic_hook 0.1.0", - "parity-dapps 1.11.0", - "parity-hash-fetch 1.11.0", - "parity-ipfs-api 1.11.0", + "parity-dapps 1.12.0", + "parity-hash-fetch 1.12.0", + "parity-ipfs-api 1.12.0", "parity-local-store 0.1.0", "parity-reactor 0.1.0", - "parity-rpc 1.11.0", + "parity-rpc 1.12.0", "parity-rpc-client 1.4.0", - "parity-updater 1.11.0", - "parity-version 1.11.0", + "parity-updater 1.12.0", + "parity-version 1.12.0", "parity-whisper 0.1.0", "parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "path 0.1.0", @@ -1931,53 +2037,58 @@ dependencies = [ "rpassword 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "rpc-cli 1.4.0", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "term_size 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "parity-clib" +version = "1.12.0" +dependencies = [ + "parity 1.12.0", ] [[package]] name = "parity-dapps" -version = "1.11.0" +version = "1.12.0" dependencies = [ "base32 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "ethcore-bytes 0.1.0", - "ethcore-devtools 1.11.0", - "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethcore-devtools 1.12.0", + "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "fetch 0.1.0", - "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 8.0.1 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11)", "jsonrpc-http-server 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11)", - "keccak-hash 0.1.0", + "keccak-hash 0.1.2", "linked-hash-map 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "mime_guess 2.0.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)", "node-health 0.1.0", "parity-dapps-glue 1.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-hash-fetch 1.11.0", + "parity-hash-fetch 1.12.0", "parity-reactor 0.1.0", - "parity-ui 1.11.0", - "parity-version 1.11.0", + "parity-version 1.12.0", "parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "registrar 0.0.1", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", "unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "zip 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", + "zip 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2010,18 +2121,19 @@ dependencies = [ [[package]] name = "parity-hash-fetch" -version = "1.11.0" +version = "1.12.0" dependencies = [ - "ethabi 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi-contract 5.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi-derive 5.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "ethabi 5.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ethabi-contract 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ethabi-derive 5.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "ethcore-bytes 0.1.0", - "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "fake-fetch 0.0.1", "fetch 0.1.0", - "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.11.24 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hash 0.1.0", + "keccak-hash 0.1.2", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "mime 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "mime_guess 2.0.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2034,12 +2146,12 @@ dependencies = [ [[package]] name = "parity-ipfs-api" -version = "1.11.0" +version = "1.12.0" dependencies = [ "cid 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore 1.11.0", + "ethcore 1.12.0", "ethcore-bytes 0.1.0", - "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 8.0.1 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11)", "jsonrpc-http-server 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11)", "multihash 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2051,16 +2163,16 @@ dependencies = [ name = "parity-local-store" version = "0.1.0" dependencies = [ - "ethcore 1.11.0", - "ethcore-io 1.11.0", + "ethcore 1.12.0", + "ethcore-io 1.12.0", "ethcore-transaction 0.1.0", "ethkey 0.3.0", "kvdb 0.1.0", "kvdb-memorydb 0.1.0", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "rlp 0.2.1", - "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2068,43 +2180,45 @@ dependencies = [ name = "parity-machine" version = "0.1.0" dependencies = [ - "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "parity-reactor" version = "0.1.0" dependencies = [ - "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "parity-rpc" -version = "1.11.0" +version = "1.12.0" dependencies = [ "ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", "cid 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "ethash 1.11.0", - "ethcore 1.11.0", + "ethash 1.12.0", + "ethcore 1.12.0", "ethcore-bytes 0.1.0", - "ethcore-devtools 1.11.0", - "ethcore-io 1.11.0", - "ethcore-light 1.11.0", - "ethcore-logger 1.11.0", - "ethcore-miner 1.11.0", - "ethcore-network 1.11.0", + "ethcore-crypto 0.1.0", + "ethcore-devtools 1.12.0", + "ethcore-io 1.12.0", + "ethcore-light 1.12.0", + "ethcore-logger 1.12.0", + "ethcore-miner 1.12.0", + "ethcore-network 1.12.0", + "ethcore-private-tx 1.0.0", + "ethcore-sync 1.12.0", "ethcore-transaction 0.1.0", - "ethcrypto 0.1.0", - "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "ethjson 0.1.0", "ethkey 0.3.0", "ethstore 0.2.0", - "ethsync 1.11.0", + "fake-fetch 0.0.1", "fetch 0.1.0", - "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "hardware-wallet 1.11.0", + "hardware-wallet 1.12.0", "itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 8.0.1 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11)", "jsonrpc-http-server 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11)", @@ -2112,7 +2226,7 @@ dependencies = [ "jsonrpc-macros 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11)", "jsonrpc-pubsub 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11)", "jsonrpc-ws-server 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11)", - "keccak-hash 0.1.0", + "keccak-hash 0.1.2", "kvdb-memorydb 0.1.0", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "macros 0.1.0", @@ -2120,23 +2234,23 @@ dependencies = [ "node-health 0.1.0", "order-stat 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "parity-reactor 0.1.0", - "parity-updater 1.11.0", - "parity-version 1.11.0", + "parity-updater 1.12.0", + "parity-version 1.12.0", "parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "patricia-trie 0.1.0", "pretty_assertions 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "rlp 0.2.1", - "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", "stats 0.1.0", "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-keccak 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-timer 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "transaction-pool 1.12.1", "transient-hashmap 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "vm 0.1.0", ] @@ -2145,15 +2259,15 @@ dependencies = [ name = "parity-rpc-client" version = "1.4.0" dependencies = [ - "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 8.0.1 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11)", "jsonrpc-ws-server 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11)", - "keccak-hash 0.1.0", + "keccak-hash 0.1.2", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-rpc 1.11.0", + "parity-rpc 1.12.0", "parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2161,90 +2275,52 @@ dependencies = [ [[package]] name = "parity-tokio-ipc" version = "0.1.5" -source = "git+https://github.com/nikvolf/parity-tokio-ipc#d6c5b3cfcc913a1b9cf0f0562a10b083ceb9fb7c" +source = "git+https://github.com/nikvolf/parity-tokio-ipc#2af3e5b6b746552d8181069a2c6be068377df1de" dependencies = [ "bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "mio-named-pipes 0.1.4 (git+https://github.com/alexcrichton/mio-named-pipes)", + "mio-named-pipes 0.1.5 (git+https://github.com/alexcrichton/mio-named-pipes)", "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-named-pipes 0.1.0 (git+https://github.com/nikvolf/tokio-named-pipes)", "tokio-uds 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "parity-ui" -version = "1.11.0" -dependencies = [ - "parity-ui-dev 1.9.0 (git+https://github.com/parity-js/shell.git?rev=eecaadcb9e421bce31e91680d14a20bbd38f92a2)", - "parity-ui-old-dev 1.9.0 (git+https://github.com/parity-js/dapp-wallet.git?rev=65deb02e7c007a0fd8aab0c089c93e3fd1de6f87)", - "parity-ui-old-precompiled 1.9.0 (git+https://github.com/js-dist-paritytech/parity-master-1-10-wallet.git?rev=4b6f112412716cd05123d32eeb7fda448288a6c6)", - "parity-ui-precompiled 1.9.0 (git+https://github.com/js-dist-paritytech/parity-master-1-10-shell.git?rev=bd25b41cd642c6b822d820dded3aa601a29aa079)", - "rustc_version 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parity-ui-dev" -version = "1.9.0" -source = "git+https://github.com/parity-js/shell.git?rev=eecaadcb9e421bce31e91680d14a20bbd38f92a2#eecaadcb9e421bce31e91680d14a20bbd38f92a2" -dependencies = [ - "parity-dapps-glue 1.9.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parity-ui-old-dev" -version = "1.9.0" -source = "git+https://github.com/parity-js/dapp-wallet.git?rev=65deb02e7c007a0fd8aab0c089c93e3fd1de6f87#65deb02e7c007a0fd8aab0c089c93e3fd1de6f87" -dependencies = [ - "parity-dapps-glue 1.9.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parity-ui-old-precompiled" -version = "1.9.0" -source = "git+https://github.com/js-dist-paritytech/parity-master-1-10-wallet.git?rev=4b6f112412716cd05123d32eeb7fda448288a6c6#4b6f112412716cd05123d32eeb7fda448288a6c6" -dependencies = [ - "parity-dapps-glue 1.9.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parity-ui-precompiled" -version = "1.9.0" -source = "git+https://github.com/js-dist-paritytech/parity-master-1-10-shell.git?rev=bd25b41cd642c6b822d820dded3aa601a29aa079#bd25b41cd642c6b822d820dded3aa601a29aa079" -dependencies = [ - "parity-dapps-glue 1.9.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "parity-updater" -version = "1.11.0" +version = "1.12.0" dependencies = [ - "ethabi 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi-contract 5.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi-derive 5.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore 1.11.0", + "ethabi 5.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ethabi-contract 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ethabi-derive 5.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethcore 1.12.0", "ethcore-bytes 0.1.0", - "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethsync 1.11.0", + "ethcore-sync 1.12.0", + "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "keccak-hash 0.1.2", + "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-hash-fetch 1.11.0", - "parity-version 1.11.0", + "matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-hash-fetch 1.12.0", + "parity-version 1.12.0", "parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "path 0.1.0", - "semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "target_info 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "parity-version" -version = "1.11.0" +version = "1.12.0" dependencies = [ "ethcore-bytes 0.1.0", "rlp 0.2.1", - "rustc_version 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "target_info 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "vergen 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2266,26 +2342,26 @@ version = "0.1.0" dependencies = [ "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore-network 1.11.0", - "ethcrypto 0.1.0", - "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethcore-crypto 0.1.0", + "ethcore-network 1.12.0", + "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "ethkey 0.3.0", "hex 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 8.0.1 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11)", "jsonrpc-macros 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11)", "jsonrpc-pubsub 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "mem 0.1.0", "ordered-float 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ring 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", "rlp 0.2.1", - "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-keccak 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2312,10 +2388,13 @@ name = "parking_lot_core" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ + "backtrace 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "petgraph 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "thread-id 3.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2329,10 +2408,10 @@ version = "0.1.0" dependencies = [ "elastic-array 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "ethcore-bytes 0.1.0", - "ethcore-logger 1.11.0", - "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethcore-logger 1.12.0", + "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "hashdb 0.1.1", - "keccak-hash 0.1.0", + "keccak-hash 0.1.2", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "memorydb 0.1.1", "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2346,6 +2425,15 @@ name = "percent-encoding" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "petgraph" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fixedbitset 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "ordermap 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "phf" version = "0.7.21" @@ -2386,7 +2474,7 @@ name = "plain_hasher" version = "0.1.0" dependencies = [ "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2404,10 +2492,11 @@ dependencies = [ [[package]] name = "price-info" -version = "1.11.0" +version = "1.12.0" dependencies = [ + "fake-fetch 0.0.1", "fetch 0.1.0", - "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.11.24 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2459,7 +2548,7 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "0.2.3" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2467,7 +2556,7 @@ dependencies = [ [[package]] name = "protobuf" -version = "1.4.1" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -2483,12 +2572,12 @@ name = "pwasm-run-test" version = "0.1.0" dependencies = [ "clap 2.29.1 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore-logger 1.11.0", - "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethcore-logger 1.12.0", + "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "ethjson 0.1.0", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", "vm 0.1.0", "wasm 0.1.0", @@ -2496,7 +2585,7 @@ dependencies = [ [[package]] name = "pwasm-utils" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2543,10 +2632,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "quote" -version = "0.4.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2570,37 +2659,37 @@ dependencies = [ [[package]] name = "rayon" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rayon-core 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rayon" -version = "0.9.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "either 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rayon-core 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon-core 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rayon-core" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "coco 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "redox_syscall" -version = "0.1.31" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "redox_termios" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "regex" @@ -2623,11 +2712,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "registrar" version = "0.0.1" dependencies = [ - "ethabi 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi-contract 5.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi-derive 5.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hash 0.1.0", + "ethabi 5.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ethabi-contract 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ethabi-derive 5.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "keccak-hash 0.1.2", ] [[package]] @@ -2635,18 +2724,19 @@ name = "relay" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "ring" version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" +source = "git+https://github.com/paritytech/ring#b98d7f586c0467d68e9946a5f47b4a04b9a86b4a" dependencies = [ - "gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", - "rayon 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tempfile 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "untrusted 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2656,7 +2746,7 @@ version = "0.2.1" dependencies = [ "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "elastic-array 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2673,9 +2763,9 @@ dependencies = [ name = "rlp_derive" version = "0.1.0" dependencies = [ - "quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "rlp 0.2.1", - "syn 0.12.14 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2693,24 +2783,12 @@ name = "rocksdb-sys" version = "0.3.0" source = "git+https://github.com/paritytech/rust-rocksdb#ecf06adf3148ab10f6f7686b724498382ff4f36e" dependencies = [ - "cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", "local-encoding 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "snappy-sys 0.1.0 (git+https://github.com/paritytech/rust-snappy)", ] -[[package]] -name = "rotor" -version = "0.6.3" -source = "git+https://github.com/tailhook/rotor#80ce2e4cd82fdc7f88bb2d737407fa5106799790" -dependencies = [ - "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", - "quick-error 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "rpassword" version = "1.0.2" @@ -2726,9 +2804,8 @@ dependencies = [ name = "rpc-cli" version = "1.4.0" dependencies = [ - "bigint 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-rpc 1.11.0", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-rpc 1.12.0", "parity-rpc-client 1.4.0", "rpassword 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2767,10 +2844,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "rustc_version" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2780,7 +2857,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "ring 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ring 0.12.1 (git+https://github.com/paritytech/ring)", "sct 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "untrusted 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "webpki 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2806,13 +2883,13 @@ name = "sct" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "ring 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ring 0.12.1 (git+https://github.com/paritytech/ring)", "untrusted 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "semver" -version = "0.6.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2825,27 +2902,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde" -version = "1.0.29" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde_derive" -version = "1.0.29" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive_internals 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.12.14 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive_internals 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_derive_internals" -version = "0.20.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.12.14 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2853,7 +2930,7 @@ name = "serde_ignored" version = "0.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2863,8 +2940,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2874,7 +2951,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "shell32-sys" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2931,7 +3008,7 @@ dependencies = [ [[package]] name = "snappy" version = "0.1.0" -source = "git+https://github.com/paritytech/rust-snappy#858eac97192ea25d18d3f3626a8cc13ca0b175bb" +source = "git+https://github.com/paritytech/rust-snappy#40ac9a0d9fd613e7f38df800a11a589b7296da73" dependencies = [ "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", "snappy-sys 0.1.0 (git+https://github.com/paritytech/rust-snappy)", @@ -2940,16 +3017,22 @@ dependencies = [ [[package]] name = "snappy-sys" version = "0.1.0" -source = "git+https://github.com/paritytech/rust-snappy#858eac97192ea25d18d3f3626a8cc13ca0b175bb" +source = "git+https://github.com/paritytech/rust-snappy#40ac9a0d9fd613e7f38df800a11a589b7296da73" dependencies = [ - "gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "spmc" -version = "0.2.2" +name = "socket2" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "stable_deref_trait" @@ -2972,18 +3055,13 @@ name = "strsim" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "subtle" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "syn" -version = "0.12.14" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -3029,10 +3107,6 @@ dependencies = [ "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "table" -version = "0.1.0" - [[package]] name = "take" version = "0.1.0" @@ -3051,6 +3125,18 @@ dependencies = [ "rand 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "tempfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "term" version = "0.4.6" @@ -3070,6 +3156,16 @@ dependencies = [ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "termion" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "textwrap" version = "0.9.0" @@ -3078,6 +3174,16 @@ dependencies = [ "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "thread-id" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "thread_local" version = "0.3.4" @@ -3092,7 +3198,7 @@ name = "threadpool" version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -3102,41 +3208,76 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "timer" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "chrono 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "tiny-keccak" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "tokio" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-tcp 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-threadpool 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-timer 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-udp 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "tokio-core" -version = "0.1.12" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", "scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-timer 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-executor" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio-io" -version = "0.1.3" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -3145,10 +3286,10 @@ version = "0.1.0" source = "git+https://github.com/nikvolf/tokio-named-pipes#0b9b728eaeb0a6673c287ac7692be398fd651752" dependencies = [ "bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", - "mio-named-pipes 0.1.4 (git+https://github.com/alexcrichton/mio-named-pipes)", - "tokio-core 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "mio-named-pipes 0.1.5 (git+https://github.com/alexcrichton/mio-named-pipes)", + "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -3156,15 +3297,39 @@ name = "tokio-proto" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "net2 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-reactor" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-retry" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -3173,9 +3338,9 @@ name = "tokio-rustls" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", "rustls 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -3184,7 +3349,33 @@ name = "tokio-service" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-tcp" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-threadpool" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-deque 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -3192,24 +3383,46 @@ name = "tokio-timer" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "tokio-timer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-udp" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "tokio-uds" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", "mio-uds 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -3217,7 +3430,7 @@ name = "toml" version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -3227,19 +3440,15 @@ dependencies = [ "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "traitobject" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "transaction-pool" -version = "1.11.0" +version = "1.12.1" dependencies = [ "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "trace-time 0.1.0", ] [[package]] @@ -3255,7 +3464,7 @@ name = "trezor-sys" version = "1.0.0" source = "git+https://github.com/paritytech/trezor-sys#8a401705e58c83db6c29c199d9577b78fde40709" dependencies = [ - "protobuf 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "protobuf 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -3263,8 +3472,8 @@ name = "trie-standardmap" version = "0.1.0" dependencies = [ "ethcore-bytes 0.1.0", - "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hash 0.1.0", + "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "keccak-hash 0.1.2", "rlp 0.2.1", ] @@ -3273,26 +3482,21 @@ name = "triehash" version = "0.1.0" dependencies = [ "elastic-array 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hash 0.1.0", + "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "keccak-hash 0.1.2", "rlp 0.2.1", "trie-standardmap 0.1.0", ] -[[package]] -name = "typeable" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "uint" -version = "0.1.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -3393,7 +3597,7 @@ name = "util-error" version = "0.1.0" dependencies = [ "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "kvdb 0.1.0", "rlp 0.2.1", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3404,15 +3608,6 @@ name = "vec_map" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "vecio" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "vergen" version = "0.1.1" @@ -3434,9 +3629,9 @@ dependencies = [ "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "common-types 0.1.0", "ethcore-bytes 0.1.0", - "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "ethjson 0.1.0", - "keccak-hash 0.1.0", + "keccak-hash 0.1.2", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "patricia-trie 0.1.0", "rlp 0.2.1", @@ -3452,23 +3647,24 @@ name = "wasm" version = "0.1.0" dependencies = [ "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore-logger 1.11.0", - "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethcore-logger 1.12.0", + "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "parity-wasm 0.27.5 (registry+https://github.com/rust-lang/crates.io-index)", - "pwasm-utils 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "pwasm-utils 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "vm 0.1.0", - "wasmi 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmi 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasmi" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "nan-preserving-float 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-wasm 0.27.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -3477,7 +3673,7 @@ name = "webpki" version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "ring 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ring 0.12.1 (git+https://github.com/paritytech/ring)", "untrusted 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -3490,6 +3686,24 @@ dependencies = [ "webpki 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "whisper-cli" +version = "0.1.0" +dependencies = [ + "docopt 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)", + "ethcore-logger 1.12.0", + "ethcore-network 1.12.0", + "ethcore-network-devp2p 1.12.0", + "jsonrpc-core 8.0.1 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11)", + "jsonrpc-http-server 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11)", + "jsonrpc-pubsub 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11)", + "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "panic_hook 0.1.0", + "parity-whisper 0.1.0", + "serde 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "winapi" version = "0.2.8" @@ -3551,45 +3765,46 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "xml-rs" -version = "0.3.6" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "xmltree" -version = "0.3.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "xml-rs 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "xml-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "zip" -version = "0.1.19" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "flate2 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", - "msdos_time 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "flate2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "msdos_time 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "podio 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)", ] [metadata] +"checksum adler32 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6cbd0b9af8587c72beadc9f72d35b9fbb070982c9e6203e46e93f10df25f8f45" "checksum aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d6531d44de723825aa81398a6415283229725a00fa30713812ab9323faa82fc4" "checksum ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6b3568b48b7cefa6b8ce125f9bb4989e52fbcc29ebea88df04cc7c5f12f70455" -"checksum app_dirs 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b7d1c0d48a81bbb13043847f957971f4d87c81542d80ece5e84ba3cba4058fd4" +"checksum app_dirs 1.2.1 (git+https://github.com/paritytech/app-dirs-rs)" = "" "checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef" +"checksum assert_matches 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "664470abf00fae0f31c0eb6e1ca12d82961b2a2541ef898bc9dd51a9254d218b" "checksum aster 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ccfdf7355d9db158df68f976ed030ab0f6578af811f5a7bb6dcf221ec24e0e0" -"checksum atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d912da0db7fa85514874458ca3651fe2cddace8d0b0505571dbdcd41ab490159" +"checksum atty 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "af80143d6f7608d746df1520709e5d141c96f240b0e62b0aa41bdfb53374d9d4" "checksum backtrace 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ebbbf59b1c43eefa8c3ede390fcc36820b4999f7914104015be25025e0d62af2" "checksum backtrace-sys 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "c63ea141ef8fdb10409d0f5daf30ac51f84ef43bff66f16627773d2a292cd189" "checksum base-x 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2f59103b47307f76e03bef1633aec7fa9e29bfb5aa6daf5a334f94233c71f6c1" "checksum base32 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1b9605ba46d61df0410d8ac686b0007add8172eba90e8e909c347856fe794d8c" "checksum base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "96434f987501f0ed4eb336a411e0631ecd1afa11574fe148587adc4ff96143c9" "checksum base64 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "229d032f1a99302697f10b27167ae6d03d49d032e6a8e2550e8d3fc13356d2b4" -"checksum bigint 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5442186ef6560f30f1ee4b9c1e4c87a35a6879d3644550cc248ec2b955eb5fcd" "checksum bincode 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e103c8b299b28a9c6990458b7013dc4a8356a9b854c51b9883241f5866fac36e" "checksum bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9bf6104718e80d7b26a68fdbacff3481cfc05df670821affc7e9cbc1884400c" "checksum bit-vec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "02b4ff8b16e6076c3e14220b39fbc1fabb6737522281a388998046859400895f" @@ -3598,16 +3813,23 @@ dependencies = [ "checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" "checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf" "checksum bn 0.4.4 (git+https://github.com/paritytech/bn)" = "" +"checksum build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39092a32794787acd8525ee150305ff051b0aa6cc2abaf193924f5ab05425f39" "checksum byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "652805b7e73fada9d85e9a6682a4abd490cb52d96aeecc12e33a0de34dfd0d23" "checksum bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "1b7db437d718977f6dc9b2e3fd6fc343c02ac6b899b73fdd2179163447bd9ce9" -"checksum cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "deaf9ec656256bb25b404c51ef50097207b9cbb29c933d31f92cae5a8a0ffee0" +"checksum cc 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)" = "8b9d2900f78631a5876dc5d6c9033ede027253efcd33dd36b1309fc6cab97ee0" "checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de" +"checksum chrono 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1cce36c92cb605414e9b824f866f5babe0a0368e39ea07393b9b63cf3844c0e6" "checksum cid 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d85ee025368e69063c420cbb2ed9f852cb03a5e69b73be021e65726ce03585b6" "checksum clap 2.29.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8f4a2b3bb7ef3c672d7c13d15613211d5a6976b6892c598b0fcb5d40765f19c2" -"checksum coco 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c06169f5beb7e31c7c67ebf5540b8b472d23e3eade3b2ec7d1f5b504a85f91bd" "checksum conv 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "78ff10625fd0ac447827aa30ea8b861fead473bb60aeb73af6c1c58caf0d1299" -"checksum cookie 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d53b80dde876f47f03cda35303e368a79b91c70b0d65ecba5fd5280944a08591" +"checksum crc 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bd5d02c0aac6bd68393ed69e00bbc2457f3e89075c6349db7189618dc4ddc1d7" "checksum crossbeam 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "24ce9782d4d5c53674646a6a4c1863a21a8fc0cb649b3c94dfc16e45071dea19" +"checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3" +"checksum crossbeam-deque 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c1bdc73742c36f7f35ebcda81dbb33a7e0d33757d03a06d9ddca762712ec5ea2" +"checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150" +"checksum crossbeam-epoch 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9b4e2817eb773f770dcb294127c011e22771899c21d18fce7dd739c0b9832e81" +"checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9" +"checksum crossbeam-utils 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d636a8b3bcc1b409d7ffd3facef8f21dcb4009626adbd0c5e6c4305c07253c7b" "checksum crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a2f4a431c5c9f662e1200b7c7f02c34e91361150e382089a8f2dec3ba680cbda" "checksum ct-logs 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "61cd11fb222fecf889f4531855c614548e92e8bd2eb178e35296885df5ee9a7c" "checksum ctrlc 1.1.1 (git+https://github.com/paritytech/rust-ctrlc.git)" = "" @@ -3622,19 +3844,20 @@ dependencies = [ "checksum env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3ddf21e73e016298f5cb37d6ef8e8da8e39f91f9ec8b0df44b7deb16a9f8cd5b" "checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3" "checksum eth-secp256k1 0.5.7 (git+https://github.com/paritytech/rust-secp256k1)" = "" -"checksum ethabi 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d8385a48c8ed984778dcca27efc0de162a191a14ed733a41a07d9b0cfaa999e" -"checksum ethabi-contract 5.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ca2263c24359e827348ac99aa1f2e28ba5bab0d6c0b83941fa252de8a9e9c073" -"checksum ethabi-derive 5.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "057f76ceb314191b2e7fcd6e2129d75816e80912e73ccc8324baa3b9e259bce3" -"checksum ethbloom 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f240172b976e2421fa5485e45cd45287bbdb56d742aa3a1d77005c49071a8518" -"checksum ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bad6fb0e4ab648f4d8d8314c340c47f9e805b085b78be1b8da4676534cb419df" +"checksum ethabi 5.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05e33a914b94b763f0a92333e4e5c95c095563f06ef7d6b295b3d3c2cf31e21f" +"checksum ethabi-contract 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "210c9e21d164c15b6ef64fe601e0e12a3c84a031d5ef558e38463e53edbd22ed" +"checksum ethabi-derive 5.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d2bc7099baa147187aedaecd9fe04a6c0541c82bc43ff317cb6900fe2b983d74" +"checksum ethbloom 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1a93a43ce2e9f09071449da36bfa7a1b20b950ee344b6904ff23de493b03b386" +"checksum ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c48729b8aea8aedb12cf4cb2e5cef439fdfe2dda4a89e47eeebd15778ef53b6" "checksum ethereum-types-serialize 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4ac59a21a9ce98e188f3dace9eb67a6c4a3c67ec7fbc7218cb827852679dc002" "checksum fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1ee15a7050e5580b3712877157068ea713b245b080ff302ae2ca973cfcd9baa" -"checksum fixed-hash 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "21c520ebc46522d519aec9cba2b7115d49cea707d771b772c46bec61aa0daeb8" -"checksum flate2 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)" = "e6234dd4468ae5d1e2dbb06fe2b058696fdc50a339c68a393aefbf00bc81e423" +"checksum fixed-hash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b18d6fd718fb4396e7a9c93ac59ba7143501467ca7a143c145b5555a571d5576" +"checksum fixedbitset 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "86d4de0081402f5e88cdac65c8dcdcc73118c1a7a465e2a05f0da05843a8ea33" +"checksum flate2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9fac2277e84e5e858483756647a9d0aa8d9a2b7cba517fd84325a0aaa69a0909" "checksum fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6cc484842f1e2884faf56f529f960cc12ad8c71ce96cc7abba0a067c98fee344" "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" -"checksum futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "0bab5b5e94f5c31fc764ba5dd9ad16568aae5d4825538c01d6bca680c9bf94a7" +"checksum futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)" = "1a70b146671de62ec8c8ed572219ca5d594d9b06c0b364d5e67b722fc559b48c" "checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" "checksum futures-timer 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a5cedfe9b6dc756220782cc1ba5bcb1fa091cdcba155e40d3556159c3db58043" "checksum gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)" = "5e33ec290da0d127825013597dbdfc28bee4964690c7ce1166cbc2a7bd08b1bb" @@ -3647,17 +3870,14 @@ dependencies = [ "checksum hex 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d6a22814455d41612f41161581c2883c0c6a1c41852729b17d5ed88f01e153aa" "checksum hidapi 0.3.1 (git+https://github.com/paritytech/hidapi-rs)" = "" "checksum httparse 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "af2f2dd97457e8fb1ae7c5a420db346af389926e36f43768b96f101546b04a07" -"checksum hyper 0.10.0-a.0 (git+https://github.com/paritytech/hyper)" = "" -"checksum hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)" = "368cb56b2740ebf4230520e2b90ebb0461e69034d85d1945febd9b3971426db2" "checksum hyper 0.11.24 (registry+https://github.com/rust-lang/crates.io-index)" = "df4dd5dae401458087396b6db7fabc4d6760aa456a5fa8e92bda549f39cae661" "checksum hyper-rustls 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d6cdc1751771a14b8175764394f025e309a28c825ed9eaf97fa62bb831dc8c5" "checksum idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "014b298351066f1512874135335d62a789ffe78a9974f94b43ed5621951eaf7d" -"checksum igd 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "356a0dc23a4fa0f8ce4777258085d00a01ea4923b2efd93538fc44bf5e1bda76" +"checksum igd 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8a254e265e8810deb357a9de757f784787ec415d056ededf410c0aa460afee9e" "checksum integer-encoding 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a053c9c7dcb7db1f2aa012c37dc176c62e4cdf14898dee0eecc606de835b8acb" "checksum interleaved-ordered 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "141340095b15ed7491bd3d4ced9d20cebfb826174b6bb03386381f62b01e3d77" "checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" "checksum ipnetwork 0.12.7 (registry+https://github.com/rust-lang/crates.io-index)" = "2134e210e2a024b5684f90e1556d5f71a1ce7f8b12e9ac9924c67fb36f63b336" -"checksum isatty 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "fa500db770a99afe2a0f2229be2a3d09c7ed9d7e4e8440bf71253141994e240f" "checksum itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4833d6978da405305126af4ac88569b5d71ff758581ce5a987dbfa3755f694fc" "checksum itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8324a32baf01e2ae060e9de58ed0bc2320c9a2833491ee36cd3b4c414de4db8c" "checksum jsonrpc-core 8.0.1 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11)" = "" @@ -3685,18 +3905,21 @@ dependencies = [ "checksum matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "100aabe6b8ff4e4a7e32c1c13523379802df0772b82466207ac25b013f193376" "checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d" "checksum memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2ffa2c986de11a9df78620c01eeaaf27d94d3ff02bf81bfcca953102dd0c6ff" +"checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" "checksum memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "71d96e3f3c0b6325d8ccd83c33b28acb183edcb6c67938ba104ec546854b0882" -"checksum mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0" "checksum mime 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e3d709ffbb330e1566dc2f2a3c9b58a5ad4a381f740b810cd305dc3f089bc160" "checksum mime_guess 2.0.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)" = "27a5e6679a0614e25adc14c6434ba84e41632b765a6d9cb2031a0cca682699ae" -"checksum miniz-sys 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "609ce024854aeb19a0ef7567d348aaa5a746b32fb72e336df7fcc16869d7e2b4" +"checksum miniz_oxide 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "aaa2d3ad070f428fffbd7d3ca2ea20bb0d8cffe9024405c44e1840bc1418b398" +"checksum miniz_oxide_c_api 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "92d98fdbd6145645828069b37ea92ca3de225e000d80702da25c20d3584b38a5" "checksum mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)" = "6d771e3ef92d58a8da8df7d6976bfca9371ed1de6619d9d5a5ce5b1f29b85bfe" -"checksum mio-named-pipes 0.1.4 (git+https://github.com/alexcrichton/mio-named-pipes)" = "" +"checksum mio-named-pipes 0.1.5 (git+https://github.com/alexcrichton/mio-named-pipes)" = "" "checksum mio-uds 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1731a873077147b626d89cc6c2a0db6288d607496c5d10c0cfcf3adc697ec673" "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" -"checksum msdos_time 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "65ba9d75bcea84e07812618fedf284a64776c2f2ea0cad6bca7f69739695a958" +"checksum miow 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9224c91f82b3c47cf53dcf78dfaa20d6888fbcc5d272d5f2fcdf8a697f3c987d" +"checksum msdos_time 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "aad9dfe950c057b1bfe9c1f2aa51583a8468ef2a5baba2ebbe06d775efeb7729" "checksum multibase 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b9c35dac080fd6e16a99924c8dfdef0af89d797dd851adab25feaffacf7850d6" "checksum multihash 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d49add5f49eb08bfc4d01ff286b84a48f53d45314f165c2d6efe477222d24f3" +"checksum nan-preserving-float 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "34d4f00fcc2f4c9efa8cc971db0da9e28290e28e97af47585e48691ef10ff31f" "checksum net2 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)" = "3a80f842784ef6c9a958b68b7516bc7e35883c614004dd94959a4dca1b716c09" "checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2" "checksum ntp 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "143149743832c6543b60a8ef2a26cd9122dfecec2b767158e852a7beecf6d7a0" @@ -3704,25 +3927,23 @@ dependencies = [ "checksum num-bigint 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "81b483ea42927c463e191802e7334556b48e7875297564c0e9951bd3a0ae53e3" "checksum num-integer 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f8d26da319fb45674985c78f1d1caf99aa4941f785d384a2ae36d0740bc3e2fe" "checksum num-iter 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "4b226df12c5a59b63569dd57fafb926d91b385dfce33d8074a412411b689d593" -"checksum num-traits 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "99843c856d68d8b4313b03a17e33c4bb42ae8f6610ea81b28abe076ac721b9b0" +"checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" "checksum num-traits 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dee092fcdf725aee04dd7da1d21debff559237d49ef1cb3e69bcb8ece44c7364" -"checksum num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "514f0d73e64be53ff320680ca671b64fe3fb91da01e1ae2ddc99eb51d453b20d" +"checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30" "checksum number_prefix 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "59a14be9c211cb9c602bad35ac99f41e9a84b44d71b8cbd3040e3bd02a214902" "checksum ole32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2c49021782e5233cd243168edfa8037574afed4eba4bbaf538b3d8d1789d8c" "checksum order-stat 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "efa535d5117d3661134dbf1719b6f0ffe06f2375843b13935db186cd094105eb" "checksum ordered-float 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "58d25b6c0e47b20d05226d288ff434940296e7e2f8b877975da32f862152241f" +"checksum ordermap 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a86ed3f5f244b372d6b1a00b72ef7f8876d0bc6a78a4c9985c53614041512063" "checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37" "checksum parity-dapps-glue 1.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "261c025c67ba416e9fe63aa9b3236520ce3c74cfbe43590c9cdcec4ccc8180e4" "checksum parity-tokio-ipc 0.1.5 (git+https://github.com/nikvolf/parity-tokio-ipc)" = "" -"checksum parity-ui-dev 1.9.0 (git+https://github.com/parity-js/shell.git?rev=eecaadcb9e421bce31e91680d14a20bbd38f92a2)" = "" -"checksum parity-ui-old-dev 1.9.0 (git+https://github.com/parity-js/dapp-wallet.git?rev=65deb02e7c007a0fd8aab0c089c93e3fd1de6f87)" = "" -"checksum parity-ui-old-precompiled 1.9.0 (git+https://github.com/js-dist-paritytech/parity-master-1-10-wallet.git?rev=4b6f112412716cd05123d32eeb7fda448288a6c6)" = "" -"checksum parity-ui-precompiled 1.9.0 (git+https://github.com/js-dist-paritytech/parity-master-1-10-shell.git?rev=bd25b41cd642c6b822d820dded3aa601a29aa079)" = "" "checksum parity-wasm 0.27.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a93ad771f67ce8a6af64c6444a99c07b15f4674203657496fc31244ffb1de2c3" "checksum parity-wordlist 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d0dec124478845b142f68b446cbee953d14d4b41f1bc0425024417720dce693" "checksum parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "9fd9d732f2de194336fb02fe11f9eed13d9e76f13f4315b4d88a14ca411750cd" "checksum parking_lot_core 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4f610cb9664da38e417ea3225f23051f589851999535290e077939838ab7a595" "checksum percent-encoding 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de154f638187706bde41d9b4738748933d64e6b37bdbffc0b47a97d16a6ae356" +"checksum petgraph 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "8b30dc85588cd02b9b76f5e386535db546d21dc68506cff2abebee0b6445e8e4" "checksum phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "cb325642290f28ee14d8c6201159949a872f220c62af6e110a56ea914fbe42fc" "checksum phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "d62594c0bb54c464f633175d502038177e90309daf2e0158be42ed5f023ce88f" "checksum phf_generator 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "6b07ffcc532ccc85e3afc45865469bf5d9e4ef5bfcf9622e3cfe80c2d275ec03" @@ -3734,49 +3955,48 @@ dependencies = [ "checksum primal-check 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8e65f96c0a171f887198c274392c99a116ef65aa7f53f3b6d4902f493965c2d1" "checksum primal-estimate 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "56ea4531dde757b56906493c8604641da14607bf9cdaa80fb9c9cabd2429f8d5" "checksum primal-sieve 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "c0911abe7b63ddec27527ba7579c3017f645eb992be6ddbfad605e34aca01876" -"checksum proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cd07deb3c6d1d9ff827999c7f9b04cdfd66b1b17ae508e14fe47b620f2282ae0" -"checksum protobuf 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "568a15e4d572d9a5e63ae3a55f84328c984842887db179b40b4cc6a608bac6a4" +"checksum proc-macro2 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "388d7ea47318c5ccdeb9ba6312cee7d3f65dd2804be8580a170fce410d50b786" +"checksum protobuf 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "40e2484e639dcae0985fc483ad76ce7ad78ee5aa092751d7d538f0b20d76486b" "checksum pulldown-cmark 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8361e81576d2e02643b04950e487ec172b687180da65c731c03cf336784e6c07" -"checksum pwasm-utils 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "54d440c3b56eee028aa5d4f18cbed8c6e0c9ae23563b93f344beb7e73854ea02" +"checksum pwasm-utils 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d51e9954a77aab7b4b606dc315a49cbed187924f163b6750cdf6d5677dbf0839" "checksum quasi 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18c45c4854d6d1cf5d531db97c75880feb91c958b0720f4ec1057135fec358b3" "checksum quasi_codegen 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "51b9e25fa23c044c1803f43ca59c98dac608976dd04ce799411edd58ece776d4" "checksum quasi_macros 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "29cec87bc2816766d7e4168302d505dd06b0a825aed41b00633d296e922e02dd" "checksum quick-error 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eda5fe9b71976e62bc81b781206aaa076401769b2143379d3eb2118388babac4" -"checksum quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1eca14c727ad12702eb4b6bfb5a232287dcf8385cb8ca83a3eeaf6519c44c408" +"checksum quote 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7b0ff51282f28dc1b53fd154298feaa2e77c5ea0dba68e1fd8b03b72fbe13d2a" "checksum rand 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)" = "512870020642bb8c221bf68baa1b2573da814f6ccfe5c9699b1c303047abe9b1" "checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5" -"checksum rayon 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b614fe08b6665cb9a231d07ac1364b0ef3cb3698f1239ee0c4c3a88a524f54c8" -"checksum rayon 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed02d09394c94ffbdfdc755ad62a132e94c3224a8354e78a1200ced34df12edf" -"checksum rayon-core 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e64b609139d83da75902f88fd6c01820046840a18471e4dfcd5ac7c0f46bea53" -"checksum redox_syscall 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "8dde11f18c108289bef24469638a04dce49da56084f2d50618b226e47eb04509" +"checksum rayon 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "80e811e76f1dbf68abf87a759083d34600017fc4e10b6bd5ad84a700f9dba4b1" +"checksum rayon-core 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9d24ad214285a7729b174ed6d3bcfcb80177807f959d95fafd5bfc5c4f201ac8" +"checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1" +"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" "checksum regex 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "744554e01ccbd98fff8c457c3b092cd67af62a555a43bfe97ae8a0451f7799fa" "checksum regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad890a5eef7953f55427c50575c680c42841653abd2b028b68cd223d157f62db" "checksum relay 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1576e382688d7e9deecea24417e350d3062d97e32e45d70b1cde65994ff1489a" -"checksum ring 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6f7d28b30a72c01b458428e0ae988d4149c20d902346902be881e3edc4bb325c" +"checksum ring 0.12.1 (git+https://github.com/paritytech/ring)" = "" "checksum rocksdb 0.4.5 (git+https://github.com/paritytech/rust-rocksdb)" = "" "checksum rocksdb-sys 0.3.0 (git+https://github.com/paritytech/rust-rocksdb)" = "" -"checksum rotor 0.6.3 (git+https://github.com/tailhook/rotor)" = "" "checksum rpassword 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b273c91bd242ca03ad6d71c143b6f17a48790e61f21a6c78568fa2b6774a24a4" "checksum rprompt 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1601f32bc5858aae3cbfa1c645c96c4d820cc5c16be0194f089560c00b6eb625" "checksum rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a" "checksum rustc-demangle 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "aee45432acc62f7b9a108cc054142dac51f979e69e71ddce7d6fc7adf29e817e" "checksum rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0ceb8ce7a5e520de349e1fa172baeba4a9e8d5ef06c47471863530bc4972ee1e" "checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" -"checksum rustc_version 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b9743a7670d88d5d52950408ecdb7c71d8986251ab604d4689dd2ca25c9bca69" +"checksum rustc_version 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a54aa04a10c68c1c4eacb4337fd883b435997ede17a9385784b990777686b09a" "checksum rustls 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cc9f2e05fd6a3ce1530cd5dbcc553d2f94d7749fe3e4f5b443668eddd842889e" "checksum safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f" "checksum scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f417c22df063e9450888a7561788e9bd46d3bb3c1466435b4eccb903807f147d" "checksum scopeguard 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c79eb2c3ac4bc2507cda80e7f3ac5b88bd8eae4c0914d5663e6a8933994be918" "checksum sct 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1137b767bbe1c4d30656993bdd97422ed41255d9400b105d735f8c7d9e800632" -"checksum semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a3186ec9e65071a2095434b1f5bb24838d4e8e130f584c790f6033c79943537" +"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)" = "4763b773978e495252615e814d2ad04773b2c1f85421c7913869a537f35cb406" -"checksum serde_derive 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)" = "8ab31f00ae5574bb643c196d5e302961c122da1c768604c6d16a35c5d551948a" -"checksum serde_derive_internals 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1fc848d073be32cd982380c06587ea1d433bc1a4c4a111de07ec2286a3ddade8" +"checksum serde 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)" = "d3bcee660dcde8f52c3765dd9ca5ee36b4bf35470a738eb0bd5a8752b0389645" +"checksum serde_derive 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)" = "f1711ab8b208541fa8de00425f6a577d90f27bb60724d2bb5fd911314af9668f" +"checksum serde_derive_internals 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)" = "89b340a48245bc03ddba31d0ff1709c118df90edc6adabaca4aac77aea181cce" "checksum serde_ignored 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "190e9765dcedb56be63b6e0993a006c7e3b071a016a304736e4a315dc01fb142" "checksum serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c9db7266c7d63a4c4b7fe8719656ccdd51acf1bed6124b174f933b009fb10bcb" "checksum sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cc30b1e1e8c40c121ca33b86c23308a090d19974ef001b4bf6e61fd1a0fb095c" -"checksum shell32-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "72f20b8f3c060374edb8046591ba28f62448c369ccbdc7b02075103fb3a9e38d" +"checksum shell32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9ee04b46101f57121c9da2b151988283b6beb79b34f5bb29a58ee48cb695122c" "checksum siphasher 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "833011ca526bd88f16778d32c699d325a9ad302fa06381cd66f7be63351d3f6d" "checksum siphasher 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0df90a788073e8d0235a67e50441d47db7c8ad9debd91cbf43736a2a92d36537" "checksum skeptic 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "24ebf8a06f5f8bae61ae5bbc7af7aac4ef6907ae975130faba1199e5fe82256a" @@ -3787,11 +4007,10 @@ dependencies = [ "checksum smallvec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8fcd03faf178110ab0334d74ca9631d77f94c8c11cc77fcb59538abf0025695d" "checksum snappy 0.1.0 (git+https://github.com/paritytech/rust-snappy)" = "" "checksum snappy-sys 0.1.0 (git+https://github.com/paritytech/rust-snappy)" = "" -"checksum spmc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "cd1f11d1fb5fd41834e55ce0b85a186efbf2f2afd9fdb09e2c8d72f9bff1ad1a" +"checksum socket2 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "06dc9f86ee48652b7c80f3d254e3b9accb67a928c562c64d10d7b016d3d98dab" "checksum stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "15132e0e364248108c5e2c02e3ab539be8d6f5d52a01ca9bbf27ed657316f02b" "checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694" -"checksum subtle 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dc7f6353c2ee5407358d063a14cccc1630804527090a6fb5a9489ce4924280fb" -"checksum syn 0.12.14 (registry+https://github.com/rust-lang/crates.io-index)" = "8c5bc2d6ff27891209efa5f63e9de78648d7801f085e4653701a692ce938d6fd" +"checksum syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "91b52877572087400e83d24b9178488541e3d535259e04ff17a63df1e5ceff59" "checksum syntex 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a8f5e3aaa79319573d19938ea38d068056b826db9883a5d47f86c1cecc688f0e" "checksum syntex_errors 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "867cc5c2d7140ae7eaad2ae9e8bf39cb18a67ca651b7834f88d46ca98faadb9c" "checksum syntex_pos 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "13ad4762fe52abc9f4008e85c4fb1b1fe3aa91ccb99ff4826a439c7c598e1047" @@ -3799,27 +4018,37 @@ dependencies = [ "checksum take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b157868d8ac1f56b64604539990685fa7611d8fa9e5476cf0c02cf34d32917c5" "checksum target_info 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c63f48baada5c52e65a29eef93ab4f8982681b67f9e8d29c7b05abcfec2b9ffe" "checksum tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "87974a6f5c1dfb344d733055601650059a3363de2a6104819293baff662132d6" +"checksum tempfile 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "11ce2fe9db64b842314052e2421ac61a73ce41b898dc8e3750398b219c5fc1e0" "checksum term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "fa63644f74ce96fbeb9b794f66aff2a52d601cbd5e80f4b97123e3899f4570f1" "checksum term_size 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9e5b9a66db815dcfd2da92db471106457082577c3c278d4138ab3e3b4e189327" +"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" "checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693" +"checksum thread-id 3.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c7fbf4c9d56b320106cd64fd024dadfa0be7cb4706725fc44a7d7ce952d820c1" "checksum thread_local 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1697c4b57aeeb7a536b647165a2825faddffb1d3bad386d507709bd51a90bb14" "checksum threadpool 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e2f0c90a5f3459330ac8bc0d2f879c693bb7a2f59689c1083fc4ef83834da865" "checksum time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)" = "d5d788d3aa77bc0ef3e9621256885555368b47bd495c13dd2e7413c89f845520" -"checksum tiny-keccak 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "58911ed5eb275a8fd2f1f0418ed360a42f59329864b64e1e95377a9024498c01" -"checksum tokio-core 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "52b4e32d8edbf29501aabb3570f027c6ceb00ccef6538f4bddba0200503e74e8" -"checksum tokio-io 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b4ab83e7adb5677e42e405fa4ceff75659d93c4d7d7dd22f52fcec59ee9f02af" +"checksum timer 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "31d42176308937165701f50638db1c31586f183f1aab416268216577aec7306b" +"checksum tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e9175261fbdb60781fcd388a4d6cc7e14764a2b629a7ad94abb439aed223a44f" +"checksum tokio 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "be15ef40f675c9fe66e354d74c73f3ed012ca1aa14d65846a33ee48f1ae8d922" +"checksum tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "aeeffbbb94209023feaef3c196a41cbcdafa06b4a6f893f68779bb5e53796f71" +"checksum tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8cac2a7883ff3567e9d66bb09100d09b33d90311feca0206c7ca034bc0c55113" +"checksum tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "6af9eb326f64b2d6b68438e1953341e00ab3cf54de7e35d92bfc73af8555313a" "checksum tokio-named-pipes 0.1.0 (git+https://github.com/nikvolf/tokio-named-pipes)" = "" "checksum tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8fbb47ae81353c63c487030659494b295f6cb6576242f907f203473b191b0389" +"checksum tokio-reactor 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3cedc8e5af5131dc3423ffa4f877cce78ad25259a9a62de0613735a13ebc64b" +"checksum tokio-retry 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f05746ae87dca83a2016b4f5dba5b237b897dd12fd324f60afe282112f16969a" "checksum tokio-rustls 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9263e472d976e4345e50c6cce4cfe6b17c71593ea593cce1df26f1efd36debb" "checksum tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "24da22d077e0f15f55162bdbdc661228c1581892f52074fb242678d015b45162" +"checksum tokio-tcp 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ec9b094851aadd2caf83ba3ad8e8c4ce65a42104f7b94d9e6550023f0407853f" +"checksum tokio-threadpool 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3d05cdd6a78005e535d2b27c21521bdf91fbb321027a62d8e178929d18966d" "checksum tokio-timer 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6131e780037787ff1b3f8aad9da83bca02438b72277850dd6ad0d455e0e20efc" +"checksum tokio-timer 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "29a89e4ad0c8f1e4c9860e605c38c69bfdad3cccd4ea446e58ff588c1c07a397" +"checksum tokio-udp 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "137bda266504893ac4774e0ec4c2108f7ccdbcb7ac8dced6305fe9e4e0b5041a" "checksum tokio-uds 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6116c71be48f8f1656551fd16458247fdd6c03201d7893ad81189055fcde03e8" "checksum toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a7540f4ffc193e0d3c94121edb19b055670d369f77d5804db11ae053a45b6e7e" -"checksum traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" "checksum transient-hashmap 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "715254c8f0811be1a79ad3ea5e6fa3c8eddec2b03d7f5ba78cf093e56d79c24f" "checksum trezor-sys 1.0.0 (git+https://github.com/paritytech/trezor-sys)" = "" -"checksum typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887" -"checksum uint 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53a4340c35703f926ec365c6797bb4a7a10bb6b9affe29ca385c9d804401f5e3" +"checksum uint 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "38051a96565903d81c9a9210ce11076b2218f3b352926baa1f5f6abbdfce8273" "checksum unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33" "checksum unicase 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "284b6d3db520d67fbe88fd778c21510d1b0ba4a551e5d0fbb023d33405f6de8a" "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" @@ -3834,11 +4063,10 @@ dependencies = [ "checksum url 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eeb819346883532a271eb626deb43c4a1bb4c4dd47c519bd78137c3e72a4fe27" "checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122" "checksum vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887b5b631c2ad01628bbbaa7dd4c869f80d3186688f8d0b6f58774fbe324988c" -"checksum vecio 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0795a11576d29ae80525a3fda315bf7b534f8feb9d34101e5fe63fb95bb2fd24" "checksum vergen 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c3365f36c57e5df714a34be40902b27a992eeddb9996eca52d0584611cf885d" "checksum version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6b772017e347561807c1aa192438c5fd74242a670a6cffacc40f2defd1dc069d" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" -"checksum wasmi 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0dedfb4cbfba1e1921b12ed05762d9d5ae99ce40e72b98bbf271561ef487e8c7" +"checksum wasmi 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46df76793c28cd8f590d5667f540a81c1c245440a17b03560e381226e27cf348" "checksum webpki 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9e1622384bcb5458c6a3e3fa572f53ea8fef1cc85e535a2983dea87e9154fac2" "checksum webpki-roots 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "155d4060e5befdf3a6076bd28c22513473d9900b763c9e4521acc6f78a75415c" "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" @@ -3849,6 +4077,6 @@ dependencies = [ "checksum ws 0.7.5 (git+https://github.com/tomusdrw/ws-rs)" = "" "checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" "checksum xdg 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a66b7c2281ebde13cf4391d70d4c7e5946c3c25e72a7b859ca8f677dcd0b0c61" -"checksum xml-rs 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7ec6c39eaa68382c8e31e35239402c0a9489d4141a8ceb0c716099a0b515b562" -"checksum xmltree 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "472a9d37c7c53ab2391161df5b89b1f3bf76dab6ab150d7941ecbdd832282082" -"checksum zip 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)" = "c0deac03fc7d43abcf19f2c2db6bd9289f9ea3d31f350e26eb0ed8b4117983c1" +"checksum xml-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c1cb601d29fe2c2ac60a2b2e5e293994d87a1f6fa9687a31a15270f909be9c2" +"checksum xmltree 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9cfb54ca6b8f17d2377219ce485b134d53561b77e1393c7ea416f543a527431" +"checksum zip 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "10931e278527cea65682696481e6d840371d581079df529ebfee186e0eaad719" diff --git a/Cargo.toml b/Cargo.toml index 293f30e032e8efe837bed1c77c598a400a38058b..8df37d5942b98f2c4a853f0dbb6e848dd00fd2b8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ description = "Parity Ethereum client" name = "parity" # NOTE Make sure to update util/version/Cargo.toml as well -version = "1.11.9" +version = "1.12.0" license = "GPL-3.0" authors = ["Parity Technologies "] @@ -17,35 +17,32 @@ textwrap = "0.9" num_cpus = "1.2" number_prefix = "0.2" rpassword = "1.0" -semver = "0.6" +semver = "0.9" ansi_term = "0.10" parking_lot = "0.5" regex = "0.2" -isatty = "0.1" +atty = "0.2.8" toml = "0.4" serde = "1.0" serde_json = "1.0" serde_derive = "1.0" -app_dirs = "1.1.1" futures = "0.1" futures-cpupool = "0.1" fdlimit = "0.1" -ws2_32-sys = "0.2" ctrlc = { git = "https://github.com/paritytech/rust-ctrlc.git" } jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.11" } -ethsync = { path = "sync" } ethcore = { path = "ethcore" } ethcore-bytes = { path = "util/bytes" } ethcore-io = { path = "util/io" } ethcore-light = { path = "ethcore/light" } ethcore-logger = { path = "logger" } -ethcore-migrations = { path = "ethcore/migrations" } ethcore-miner = { path = "miner" } ethcore-network = { path = "util/network" } +ethcore-private-tx = { path = "ethcore/private-tx" } ethcore-service = { path = "ethcore/service" } -ethcore-stratum = { path = "stratum" } +ethcore-sync = { path = "ethcore/sync" } ethcore-transaction = { path = "ethcore/transaction" } -ethereum-types = "0.2" +ethereum-types = "0.3" node-filter = { path = "ethcore/node_filter" } ethkey = { path = "ethkey" } node-health = { path = "dapps/node-health" } @@ -64,10 +61,11 @@ path = { path = "util/path" } dir = { path = "util/dir" } panic_hook = { path = "util/panic_hook" } keccak-hash = { path = "util/hash" } -migration = { path = "util/migration" } +migration-rocksdb = { path = "util/migration-rocksdb" } kvdb = { path = "util/kvdb" } kvdb-rocksdb = { path = "util/kvdb-rocksdb" } journaldb = { path = "util/journaldb" } +mem = { path = "util/mem" } parity-dapps = { path = "dapps", optional = true } ethcore-secretstore = { path = "secret_store", optional = true } @@ -81,26 +79,17 @@ rustc_version = "0.2" pretty_assertions = "0.1" ipnetwork = "0.12.6" tempdir = "0.3" +fake-fetch = { path = "util/fake-fetch" } [target.'cfg(windows)'.dependencies] -winapi = "0.2" +winapi = { version = "0.3.4", features = ["winsock2", "winuser", "shellapi"] } [target.'cfg(not(windows))'.dependencies] daemonize = { git = "https://github.com/paritytech/daemonize" } [features] -default = ["ui-precompiled"] -ui = [ - "ui-enabled", - "parity-dapps/ui", -] -ui-precompiled = [ - "ui-enabled", - "parity-dapps/ui-precompiled", -] -ui-enabled = ["dapps"] +default = ["dapps"] dapps = ["parity-dapps"] -jit = ["ethcore/jit"] json-tests = ["ethcore/json-tests"] test-heavy = ["ethcore/test-heavy"] evm-debug = ["ethcore/evm-debug"] @@ -108,6 +97,17 @@ evm-debug-tests = ["ethcore/evm-debug-tests"] slow-blocks = ["ethcore/slow-blocks"] secretstore = ["ethcore-secretstore"] final = ["parity-version/final"] +deadlock_detection = ["parking_lot/deadlock_detection"] +# to create a memory profile (requires nightly rust), use e.g. +# `heaptrack /path/to/parity `, +# to visualize a memory profile, use `heaptrack_gui` +# or +# `valgrind --tool=massif /path/to/parity ` +# and `massif-visualizer` for visualization +memory_profiling = [] + +[lib] +path = "parity/lib.rs" [[bin]] path = "parity/main.rs" @@ -131,6 +131,11 @@ members = [ "ethstore/cli", "evmbin", "miner", + "parity-clib", "transaction-pool", - "whisper" + "whisper", + "whisper/cli", ] + +[patch.crates-io] +ring = { git = "https://github.com/paritytech/ring" } diff --git a/README.md b/README.md index 1e4a12bf02d78d52afcd3ac5c924e27cdcfcec8e..95e5ba2ed2dfafb558349b69e0834f2542a99e47 100644 --- a/README.md +++ b/README.md @@ -19,9 +19,7 @@ Get in touch with us on Gitter: Or 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) -Official website: https://parity.io - -Be sure to check out [our wiki](https://wiki.parity.io) for more information. +Official website: https://parity.io | Be sure to check out [our wiki](https://wiki.parity.io) for more information. ---- @@ -29,28 +27,19 @@ Be sure to check out [our wiki](https://wiki.parity.io) for more information. Parity's goal is to be the fastest, lightest, and most secure Ethereum client. We are developing Parity using the sophisticated and cutting-edge Rust programming language. Parity is licensed under the GPLv3, and can be used for all your Ethereum needs. -Parity comes with a built-in wallet, to install it please follow [these instructions](https://wiki.parity.io/Parity-Wallet). It includes various functionality allowing you to: - -- create and manage your Ethereum accounts; -- manage your Ether and any Ethereum tokens; -- create and register your own tokens; -- and much more. - -From Parity Ethereum client version >=1.10, the User Interface (UI) is accessible in a separate application called Parity UI. To keep using the UI in the browser (deprecated), [follow these steps](https://wiki.parity.io/FAQ-Basic-Operations,-Configuration,-and-Synchronization.md#the-parity-ui-application-isnt-working-the-way-i-want). +From Parity Ethereum client version 1.10.0, the User Interface (UI) is accessible in a separate application called Parity UI. To keep using the UI in the browser (deprecated), [follow these steps](https://wiki.parity.io/FAQ-Basic-Operations,-Configuration,-and-Synchronization#the-parity-ui-application-isnt-working-the-way-i-want). By default, Parity will also run a JSONRPC server on `127.0.0.1:8545` and a websockets server on `127.0.0.1:8546`. This is fully configurable and supports a number of APIs. -If you run into an issue while using Parity, feel free to file one in this repository 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! +If you run into an issue while using Parity, feel free to file one in this repository 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). -**For security-critical issues**, please refer to the security policy outlined in [SECURITY.MD](SECURITY.md). - -Parity's current release is 1.9. You can download it at https://github.com/paritytech/parity/releases or follow the instructions below to build from source. +Parity's current beta-release is 1.11. You can download it at https://github.com/paritytech/parity/releases or follow the instructions below to build from source. ---- ## Build dependencies -**Parity requires Rust version 1.23.0 to build** +**Parity requires Rust version 1.26.0 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: @@ -59,7 +48,7 @@ We recommend installing Rust through [rustup](https://www.rustup.rs/). If you do $ curl https://sh.rustup.rs -sSf | sh ``` - Parity also requires `gcc`, `g++`, `libssl-dev`/`openssl`, `libudev-dev` and `pkg-config` packages to be installed. + Parity also requires `gcc`, `g++`, `libssl-dev`/`openssl`, `libudev-dev`, `pkg-config`, `file` and `make` packages to be installed. - OSX: ```bash @@ -75,7 +64,11 @@ We recommend installing Rust through [rustup](https://www.rustup.rs/). If you do $ rustup default stable-x86_64-pc-windows-msvc ``` -Once you have rustup, install Parity or download and build from source +Once you have rustup installed, then you need to install: +* [Perl](https://www.perl.org) +* [Yasm](http://yasm.tortall.net) + +Make sure that these binaries are in your `PATH`. After that you should be able to build parity from source. ---- @@ -120,13 +113,7 @@ Note: if cargo fails to parse manifest try: $ ~/.cargo/bin/cargo build --release ``` -Note: When compiling a crate and you receive the following error: - -``` -error: the crate is compiled with the panic strategy `abort` which is incompatible with this crate's strategy of `unwind` -``` - -Cleaning the repository will most likely solve the issue, try: +Note, when compiling a crate and you receive errors, it's in most cases your outdated version of Rust, or some of your crates have to be recompiled. Cleaning the repository will most likely solve the issue if you are on the latest stable version of Rust, try: ```bash $ cargo clean @@ -151,13 +138,13 @@ first. ## Simple one-line installer for Mac and Ubuntu ```bash -bash <(curl https://get.parity.io -Lk) +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 -Lk) -r stable +bash <(curl https://get.parity.io -L) -r stable ``` ## Start Parity diff --git a/chainspec/src/main.rs b/chainspec/src/main.rs index bcef53f3f09835d2e8483ea983f8c7fc055079af..708d74b503da4ed93b0113f9aa2b9f3a4d05b310 100644 --- a/chainspec/src/main.rs +++ b/chainspec/src/main.rs @@ -1,3 +1,19 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + extern crate serde_json; extern crate serde_ignored; extern crate ethjson; diff --git a/dapps/Cargo.toml b/dapps/Cargo.toml index e5b25a203f9665c22c775a1a884fd4754a5cc130..c3fd75ddc10f41eb0cefc78c8ac47069438f241a 100644 --- a/dapps/Cargo.toml +++ b/dapps/Cargo.toml @@ -1,7 +1,7 @@ [package] description = "Parity Dapps crate" name = "parity-dapps" -version = "1.11.0" +version = "1.12.0" license = "GPL-3.0" authors = ["Parity Technologies "] @@ -22,19 +22,18 @@ serde = "1.0" serde_derive = "1.0" serde_json = "1.0" unicase = "1.4" -zip = { version = "0.1", default-features = false } +zip = { version = "0.3", default-features = false, features = ["deflate"] } itertools = "0.5" jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.11" } jsonrpc-http-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.11" } ethcore-bytes = { path = "../util/bytes" } -ethereum-types = "0.2" +ethereum-types = "0.3" fetch = { path = "../util/fetch" } node-health = { path = "./node-health" } parity-hash-fetch = { path = "../hash-fetch" } parity-reactor = { path = "../util/reactor" } -parity-ui = { path = "./ui" } keccak-hash = { path = "../util/hash" } parity-version = { path = "../util/version" } registrar = { path = "../registrar" } @@ -42,7 +41,3 @@ registrar = { path = "../registrar" } [dev-dependencies] env_logger = "0.4" ethcore-devtools = { path = "../devtools" } - -[features] -ui = ["parity-ui/no-precompiled-js"] -ui-precompiled = ["parity-ui/use-precompiled-js"] diff --git a/dapps/js-glue/build.rs b/dapps/js-glue/build.rs index 442abf7dfba6404c05fbf2923dbe67346457b1b1..19d422ab2311d957392d619206f827c6f3f66ab6 100644 --- a/dapps/js-glue/build.rs +++ b/dapps/js-glue/build.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -14,7 +14,6 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . - #[cfg(feature = "with-syntex")] mod inner { extern crate syntex; diff --git a/dapps/js-glue/src/build.rs b/dapps/js-glue/src/build.rs index 31f27306a9defa591550af7d097ab4278dafe69a..76b0a8714ce24eb8c31e7e9a1736b31a682f1a07 100644 --- a/dapps/js-glue/src/build.rs +++ b/dapps/js-glue/src/build.rs @@ -1,3 +1,18 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . #[cfg(feature = "with-syntex")] pub mod inner { diff --git a/dapps/js-glue/src/codegen.rs b/dapps/js-glue/src/codegen.rs index c6e948820fce03ed2baabc734ddbe0bf94a7b7bf..4b6c4445d74ec9e3afce5154e14f168a5f95dbb3 100644 --- a/dapps/js-glue/src/codegen.rs +++ b/dapps/js-glue/src/codegen.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/dapps/js-glue/src/js.rs b/dapps/js-glue/src/js.rs index d1d1cdda9139dc3a8307a42904ecd8185fd3b6b0..906b238ec723e848bf3f4731b2d7c8277dc98488 100644 --- a/dapps/js-glue/src/js.rs +++ b/dapps/js-glue/src/js.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -14,8 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -#![cfg_attr(feature="use-precompiled-js", allow(dead_code))] -#![cfg_attr(feature="use-precompiled-js", allow(unused_imports))] +#![cfg_attr(feature = "use-precompiled-js", allow(dead_code))] +#![cfg_attr(feature = "use-precompiled-js", allow(unused_imports))] use std::fmt; use std::process::Command; diff --git a/dapps/js-glue/src/lib.rs b/dapps/js-glue/src/lib.rs index 143dd1fc8bf7b650befa986b17c6041bbeb6ffa0..f8ada2541e96e388ad9d15f16f3e78cbe8317581 100644 --- a/dapps/js-glue/src/lib.rs +++ b/dapps/js-glue/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -14,7 +14,6 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . - #![cfg_attr(not(feature = "with-syntex"), feature(rustc_private, plugin))] #![cfg_attr(not(feature = "with-syntex"), plugin(quasi_macros))] diff --git a/dapps/js-glue/src/lib.rs.in b/dapps/js-glue/src/lib.rs.in index 99a253013d1388d048a8401f675b1bc6f5aeea00..b78eae109843a86f18bb00970c482209204b4ac0 100644 --- a/dapps/js-glue/src/lib.rs.in +++ b/dapps/js-glue/src/lib.rs.in @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -8,11 +8,12 @@ // 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 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity. If not, see . +// along with Parity. If not, see . + extern crate quasi; diff --git a/dapps/node-health/src/health.rs b/dapps/node-health/src/health.rs index ec53d2e29441e6c01c6742c90b543bae701bcc72..430061ea2bbbee6e425ef96ff3d6fe565e11f4aa 100644 --- a/dapps/node-health/src/health.rs +++ b/dapps/node-health/src/health.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -17,7 +17,7 @@ //! Reporting node's health. use std::sync::Arc; -use std::time; +use std::time::Duration; use futures::Future; use futures::sync::oneshot; use types::{HealthInfo, HealthStatus, Health}; @@ -26,7 +26,7 @@ use parity_reactor::Remote; use parking_lot::Mutex; use {SyncStatus}; -const TIMEOUT_SECS: u64 = 5; +const TIMEOUT: Duration = Duration::from_secs(5); const PROOF: &str = "Only one closure is invoked."; /// A struct enabling you to query for node's health. @@ -53,11 +53,11 @@ impl NodeHealth { let tx = Arc::new(Mutex::new(Some(tx))); let tx2 = tx.clone(); self.remote.spawn_with_timeout( - move || time.then(move |result| { + move |_| time.then(move |result| { let _ = tx.lock().take().expect(PROOF).send(Ok(result)); Ok(()) }), - time::Duration::from_secs(TIMEOUT_SECS), + TIMEOUT, move || { let _ = tx2.lock().take().expect(PROOF).send(Err(())); }, diff --git a/dapps/node-health/src/lib.rs b/dapps/node-health/src/lib.rs index b0eb133ee38c933f2ef9c992937e9851cb01b243..13529004a6c9442246be13af9b7bff616812956b 100644 --- a/dapps/node-health/src/lib.rs +++ b/dapps/node-health/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/dapps/node-health/src/time.rs b/dapps/node-health/src/time.rs index c3da050a47fddc63d4403ed1a03c4e1957933d62..9dfb3aa87f47e479b025d4cf4b7039b1a40468ef 100644 --- a/dapps/node-health/src/time.rs +++ b/dapps/node-health/src/time.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/dapps/node-health/src/types.rs b/dapps/node-health/src/types.rs index ae883a626b9ecf99b18c6d2f768ce820952f6901..76fd3682f9629b8247501016bef34fd7cc43c474 100644 --- a/dapps/node-health/src/types.rs +++ b/dapps/node-health/src/types.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/dapps/src/api/api.rs b/dapps/src/api/api.rs index a9f9af293b0e0acad5780706214a15556f3ae3af..e6bba899f9a75e1cf9db4ce94c169bb7e2b82653 100644 --- a/dapps/src/api/api.rs +++ b/dapps/src/api/api.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/dapps/src/api/mod.rs b/dapps/src/api/mod.rs index 4ffb9f791a72a43a666ee2a2ab30e7aa469d10e3..c18eb189ea5dbd0cc8adc7cb0da348b6b258a0d0 100644 --- a/dapps/src/api/mod.rs +++ b/dapps/src/api/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/dapps/src/api/response.rs b/dapps/src/api/response.rs index c8d25c14450060896a2224f9cd0351d4b7b03fa7..5fe81eaa19db2301343f6748cad610402b427351 100644 --- a/dapps/src/api/response.rs +++ b/dapps/src/api/response.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/dapps/src/api/types.rs b/dapps/src/api/types.rs index 6beca3b5867d8418c9b11f8cf37366eb11fea0c1..8bc451a849d7ff8a09015e7e0e30889bc4c1be3f 100644 --- a/dapps/src/api/types.rs +++ b/dapps/src/api/types.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/dapps/src/apps/app.rs b/dapps/src/apps/app.rs index c75346124c72bbe10bd518cc4905ffe8c187c57b..15468b4f1d62fca5d0a5f6d6772922ee76a00584 100644 --- a/dapps/src/apps/app.rs +++ b/dapps/src/apps/app.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/dapps/src/apps/cache.rs b/dapps/src/apps/cache.rs index c81d4d9af9daf70ec956fe9d134cb66b1181699c..b93acfaece10f1c1a4ecc96e9c753d9382f8b420 100644 --- a/dapps/src/apps/cache.rs +++ b/dapps/src/apps/cache.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/dapps/src/apps/fetcher/installers.rs b/dapps/src/apps/fetcher/installers.rs index 2c067d493c9d68790c23918862b2f344eb45c423..9fba80aabec04b55157a0a9289e522c8066183b7 100644 --- a/dapps/src/apps/fetcher/installers.rs +++ b/dapps/src/apps/fetcher/installers.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -21,13 +21,12 @@ use std::path::PathBuf; use ethereum_types::H256; use fetch; use futures_cpupool::CpuPool; -use hash::keccak_buffer; +use hash::keccak_pipe; use mime_guess::Mime; use apps::manifest::{MANIFEST_FILENAME, deserialize_manifest, serialize_manifest, Manifest}; use handlers::{ContentValidator, ValidatorResponse}; use page::{local, PageCache}; -use Embeddable; type OnDone = Box) + Send>; @@ -55,15 +54,15 @@ fn write_response_and_check_hash( // Now write the response let mut file = io::BufWriter::new(fs::File::create(&content_path)?); let mut reader = io::BufReader::new(fetch::BodyReader::new(response)); - io::copy(&mut reader, &mut file)?; + let hash = keccak_pipe(&mut reader, &mut file)?; + let mut file = file.into_inner()?; file.flush()?; // Validate hash - // TODO [ToDr] calculate keccak in-flight while reading the response - let mut file = io::BufReader::new(fs::File::open(&content_path)?); - let hash = keccak_buffer(&mut file)?; if id == hash { - Ok((file.into_inner(), content_path)) + // The writing above changed the file Read position, which we need later. So we just create a new file handle + // here. + Ok((fs::File::open(&content_path)?, content_path)) } else { Err(ValidationError::HashMismatch { expected: id, @@ -124,17 +123,15 @@ pub struct Dapp { id: String, dapps_path: PathBuf, on_done: OnDone, - embeddable_on: Embeddable, pool: CpuPool, } impl Dapp { - pub fn new(id: String, dapps_path: PathBuf, on_done: OnDone, embeddable_on: Embeddable, pool: CpuPool) -> Self { + pub fn new(id: String, dapps_path: PathBuf, on_done: OnDone, pool: CpuPool) -> Self { Dapp { id, dapps_path, on_done, - embeddable_on, pool, } } @@ -170,7 +167,6 @@ impl ContentValidator for Dapp { fn validate_and_install(self, response: fetch::Response) -> Result { let id = self.id.clone(); let pool = self.pool; - let embeddable_on = self.embeddable_on; let validate = move |dapp_path: PathBuf| { let (file, zip_path) = write_response_and_check_hash(&id, dapp_path.clone(), &format!("{}.zip", id), response)?; trace!(target: "dapps", "Opening dapp bundle at {:?}", zip_path); @@ -210,7 +206,7 @@ impl ContentValidator for Dapp { let mut manifest_file = fs::File::create(manifest_path)?; manifest_file.write_all(manifest_str.as_bytes())?; // Create endpoint - let endpoint = local::Dapp::new(pool, dapp_path, manifest.into(), PageCache::Enabled, embeddable_on); + let endpoint = local::Dapp::new(pool, dapp_path, manifest.into(), PageCache::Enabled); Ok(endpoint) }; @@ -266,3 +262,9 @@ impl From for ValidationError { ValidationError::Zip(err) } } + +impl From>> for ValidationError { + fn from(err: io::IntoInnerError>) -> Self { + ValidationError::Io(err.into()) + } +} diff --git a/dapps/src/apps/fetcher/mod.rs b/dapps/src/apps/fetcher/mod.rs index 8ed3024fdf2a1c0b7dffb2972c1de1d32194f86c..a7afd91eed1e8851694e5e8f9f0d10635944e5c9 100644 --- a/dapps/src/apps/fetcher/mod.rs +++ b/dapps/src/apps/fetcher/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -31,7 +31,7 @@ use hash_fetch::urlhint::{URLHintContract, URLHint, URLHintResult}; use hyper::StatusCode; use ethereum_types::H256; -use {Embeddable, SyncStatus, random_filename}; +use {SyncStatus, random_filename}; use parking_lot::Mutex; use page::local; use handlers::{ContentHandler, ContentFetcherHandler}; @@ -50,7 +50,6 @@ pub struct ContentFetcher>, sync: Arc, - embeddable_on: Embeddable, fetch: F, pool: CpuPool, only_content: bool, @@ -78,7 +77,6 @@ impl ContentFetcher { resolver, sync, cache: Arc::new(Mutex::new(ContentCache::default())), - embeddable_on: None, fetch, pool, only_content: true, @@ -90,38 +88,30 @@ impl ContentFetcher { self } - pub fn embeddable_on(mut self, embeddable_on: Embeddable) -> Self { - self.embeddable_on = embeddable_on; - self - } - - fn not_found(embeddable: Embeddable) -> endpoint::Response { + fn not_found() -> endpoint::Response { Box::new(future::ok(ContentHandler::error( StatusCode::NotFound, "Resource Not Found", "Requested resource was not found.", None, - embeddable, ).into())) } - fn still_syncing(embeddable: Embeddable) -> endpoint::Response { + fn still_syncing() -> endpoint::Response { Box::new(future::ok(ContentHandler::error( StatusCode::ServiceUnavailable, "Sync In Progress", "Your node is still syncing. We cannot resolve any content before it's fully synced.", Some("Refresh"), - embeddable, ).into())) } - fn dapps_disabled(address: Embeddable) -> endpoint::Response { + fn dapps_disabled() -> endpoint::Response { Box::new(future::ok(ContentHandler::error( StatusCode::ServiceUnavailable, "Network Dapps Not Available", "This interface doesn't support network dapps for security reasons.", None, - address, ).into())) } @@ -195,10 +185,10 @@ impl Endpoint for ContentFetcher { match content { // Don't serve dapps if we are still syncing (but serve content) Some(URLHintResult::Dapp(_)) if self.sync.is_major_importing() => { - (None, Self::still_syncing(self.embeddable_on.clone())) + (None, Self::still_syncing()) }, Some(URLHintResult::Dapp(_)) if self.only_content => { - (None, Self::dapps_disabled(self.embeddable_on.clone())) + (None, Self::dapps_disabled()) }, Some(content) => { let handler = match content { @@ -211,10 +201,8 @@ impl Endpoint for ContentFetcher { content_id.clone(), self.cache_path.clone(), Box::new(on_done), - self.embeddable_on.clone(), self.pool.clone(), ), - self.embeddable_on.clone(), self.fetch.clone(), self.pool.clone(), ) @@ -228,10 +216,8 @@ impl Endpoint for ContentFetcher { content_id.clone(), self.cache_path.clone(), Box::new(on_done), - self.embeddable_on.clone(), self.pool.clone(), ), - self.embeddable_on.clone(), self.fetch.clone(), self.pool.clone(), ) @@ -248,7 +234,6 @@ impl Endpoint for ContentFetcher { Box::new(on_done), self.pool.clone(), ), - self.embeddable_on.clone(), self.fetch.clone(), self.pool.clone(), ) @@ -258,12 +243,12 @@ impl Endpoint for ContentFetcher { (Some(ContentStatus::Fetching(handler.fetch_control())), Box::new(handler) as endpoint::Response) }, None if self.sync.is_major_importing() => { - (None, Self::still_syncing(self.embeddable_on.clone())) + (None, Self::still_syncing()) }, None => { // This may happen when sync status changes in between // `contains` and `to_handler` - (None, Self::not_found(self.embeddable_on.clone())) + (None, Self::not_found()) }, } }, @@ -330,7 +315,7 @@ mod tests { icon_url: "".into(), local_url: Some("".into()), allow_js_eval: None, - }, Default::default(), None); + }, Default::default()); // when fetcher.set_status("test", ContentStatus::Ready(handler)); diff --git a/dapps/src/apps/fs.rs b/dapps/src/apps/fs.rs index 3d93a2fae1a372221d3520bcfa82d16c158deafa..975f3067eeeac912c10c3d34fecd2543a8076e46 100644 --- a/dapps/src/apps/fs.rs +++ b/dapps/src/apps/fs.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -24,7 +24,6 @@ use futures_cpupool::CpuPool; use apps::manifest::{MANIFEST_FILENAME, deserialize_manifest}; use endpoint::{Endpoint, EndpointInfo}; use page::{local, PageCache}; -use Embeddable; struct LocalDapp { id: String, @@ -65,20 +64,19 @@ fn read_manifest(name: &str, mut path: PathBuf) -> EndpointInfo { /// Returns Dapp Id and Local Dapp Endpoint for given filesystem path. /// Parses the path to extract last component (for name). /// `None` is returned when path is invalid or non-existent. -pub fn local_endpoint>(path: P, embeddable: Embeddable, pool: CpuPool) -> Option<(String, Box)> { +pub fn local_endpoint>(path: P, pool: CpuPool) -> Option<(String, Box)> { let path = path.as_ref().to_owned(); path.canonicalize().ok().and_then(|path| { let name = path.file_name().and_then(|name| name.to_str()); name.map(|name| { let dapp = local_dapp(name.into(), path.clone()); (dapp.id, Box::new(local::Dapp::new( - pool.clone(), dapp.path, dapp.info, PageCache::Disabled, embeddable.clone()) + pool.clone(), dapp.path, dapp.info, PageCache::Disabled) )) }) }) } - fn local_dapp(name: String, path: PathBuf) -> LocalDapp { // try to get manifest file let info = read_manifest(&name, path.clone()); @@ -91,18 +89,17 @@ fn local_dapp(name: String, path: PathBuf) -> LocalDapp { /// Returns endpoints for Local Dapps found for given filesystem path. /// Scans the directory and collects `local::Dapp`. -pub fn local_endpoints>(dapps_path: P, embeddable: Embeddable, pool: CpuPool) -> BTreeMap> { +pub fn local_endpoints>(dapps_path: P, pool: CpuPool) -> BTreeMap> { let mut pages = BTreeMap::>::new(); for dapp in local_dapps(dapps_path.as_ref()) { pages.insert( dapp.id, - Box::new(local::Dapp::new(pool.clone(), dapp.path, dapp.info, PageCache::Disabled, embeddable.clone())) + Box::new(local::Dapp::new(pool.clone(), dapp.path, dapp.info, PageCache::Disabled)) ); } pages } - fn local_dapps(dapps_path: &Path) -> Vec { let files = fs::read_dir(dapps_path); if let Err(e) = files { diff --git a/dapps/src/apps/manifest.rs b/dapps/src/apps/manifest.rs index e320482195da5c1feb093ef29368fb1390054869..4d71af40fe0b208709dd04d70e7e9143ca3f0ebc 100644 --- a/dapps/src/apps/manifest.rs +++ b/dapps/src/apps/manifest.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/dapps/src/apps/mod.rs b/dapps/src/apps/mod.rs index c065a08474cc4735b413edc7e4adfdd0723edf0e..3fe394b6de21cfe8cc1fd6483575aa7e459446bd 100644 --- a/dapps/src/apps/mod.rs +++ b/dapps/src/apps/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -17,19 +17,15 @@ use std::path::PathBuf; use std::sync::Arc; -use endpoint::{Endpoints, Endpoint}; +use endpoint::Endpoints; use futures_cpupool::CpuPool; -use page; use proxypac::ProxyPac; use web::Web; use fetch::Fetch; -use parity_dapps::WebApp; -use parity_ui; -use {WebProxyTokens, ParentFrameSettings}; +use WebProxyTokens; mod app; mod cache; -mod ui; pub mod fs; pub mod fetcher; pub mod manifest; @@ -37,64 +33,38 @@ pub mod manifest; pub use self::app::App; pub const HOME_PAGE: &'static str = "home"; -pub const RPC_PATH: &'static str = "rpc"; -pub const API_PATH: &'static str = "api"; -pub const UTILS_PATH: &'static str = "parity-utils"; +pub const RPC_PATH: &'static str = "rpc"; +pub const API_PATH: &'static str = "api"; pub const WEB_PATH: &'static str = "web"; pub const URL_REFERER: &'static str = "__referer="; -pub fn utils(pool: CpuPool) -> Box { - Box::new(page::builtin::Dapp::new(pool, parity_ui::App::default())) -} - -pub fn ui(pool: CpuPool) -> Box { - Box::new(page::builtin::Dapp::with_fallback_to_index(pool, parity_ui::App::default())) -} - -pub fn ui_redirection(embeddable: Option) -> Box { - Box::new(ui::Redirection::new(embeddable)) -} - pub fn all_endpoints( dapps_path: PathBuf, extra_dapps: Vec, dapps_domain: &str, - embeddable: Option, web_proxy_tokens: Arc, fetch: F, pool: CpuPool, ) -> (Vec, Endpoints) { // fetch fs dapps at first to avoid overwriting builtins - let mut pages = fs::local_endpoints(dapps_path.clone(), embeddable.clone(), pool.clone()); + let mut pages = fs::local_endpoints(dapps_path.clone(), pool.clone()); let local_endpoints: Vec = pages.keys().cloned().collect(); for path in extra_dapps { - if let Some((id, endpoint)) = fs::local_endpoint(path.clone(), embeddable.clone(), pool.clone()) { + if let Some((id, endpoint)) = fs::local_endpoint(path.clone(), pool.clone()) { pages.insert(id, endpoint); } else { warn!(target: "dapps", "Ignoring invalid dapp at {}", path.display()); } } - // NOTE [ToDr] Dapps will be currently embeded on 8180 - insert::(&mut pages, "ui", Embeddable::Yes(embeddable.clone()), pool.clone()); - // old version - insert::(&mut pages, "v1", Embeddable::Yes(embeddable.clone()), pool.clone()); - - pages.insert("proxy".into(), ProxyPac::boxed(embeddable.clone(), dapps_domain.to_owned())); - pages.insert(WEB_PATH.into(), Web::boxed(embeddable.clone(), web_proxy_tokens.clone(), fetch.clone(), pool.clone())); + pages.insert( + "proxy".into(), + ProxyPac::boxed(dapps_domain.to_owned()) + ); + pages.insert( + WEB_PATH.into(), + Web::boxed(web_proxy_tokens.clone(), fetch.clone(), pool.clone()) + ); (local_endpoints, pages) } - -fn insert(pages: &mut Endpoints, id: &str, embed_at: Embeddable, pool: CpuPool) { - pages.insert(id.to_owned(), Box::new(match embed_at { - Embeddable::Yes(address) => page::builtin::Dapp::new_safe_to_embed(pool, T::default(), address), - Embeddable::No => page::builtin::Dapp::new(pool, T::default()), - })); -} - -enum Embeddable { - Yes(Option), - #[allow(dead_code)] - No, -} diff --git a/dapps/src/apps/ui.rs b/dapps/src/apps/ui.rs deleted file mode 100644 index 39da14e5b97509e918e821b702152ae51c8904db..0000000000000000000000000000000000000000 --- a/dapps/src/apps/ui.rs +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2015-2017 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 . - -//! UI redirections - -use hyper::StatusCode; -use futures::future; - -use endpoint::{Endpoint, Request, Response, EndpointPath}; -use {handlers, Embeddable}; - -/// Redirection to UI server. -pub struct Redirection { - embeddable_on: Embeddable, -} - -impl Redirection { - pub fn new( - embeddable_on: Embeddable, - ) -> Self { - Redirection { - embeddable_on, - } - } -} - -impl Endpoint for Redirection { - fn respond(&self, _path: EndpointPath, req: Request) -> Response { - Box::new(future::ok(if let Some(ref frame) = self.embeddable_on { - trace!(target: "dapps", "Redirecting to signer interface."); - let protocol = req.uri().scheme().unwrap_or("http"); - handlers::Redirection::new(format!("{}://{}:{}", protocol, &frame.host, frame.port)).into() - } else { - trace!(target: "dapps", "Signer disabled, returning 404."); - handlers::ContentHandler::error( - StatusCode::NotFound, - "404 Not Found", - "Your homepage is not available when Trusted Signer is disabled.", - Some("You can still access dapps by writing a correct address, though. Re-enable Signer to get your homepage back."), - None, - ).into() - })) - } -} diff --git a/dapps/src/endpoint.rs b/dapps/src/endpoint.rs index fd05445c2a240105073eb1dd7f283f79174b299a..948f412b3810df8110b799be5849def1ae82183d 100644 --- a/dapps/src/endpoint.rs +++ b/dapps/src/endpoint.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/dapps/src/error_tpl.html b/dapps/src/error_tpl.html index c6b4db0e7f5508b33a3cd349c414a99c65a84fd5..4b155cf35d641ddb210d8cffa0296deaaad724d0 100644 --- a/dapps/src/error_tpl.html +++ b/dapps/src/error_tpl.html @@ -4,7 +4,88 @@ {title} - +
diff --git a/dapps/src/handlers/content.rs b/dapps/src/handlers/content.rs index c7eccf474e44daae2904f0d656ff8d1be09f6e00..9449f0f796bc067f320fd046800a9e68c6139ed7 100644 --- a/dapps/src/handlers/content.rs +++ b/dapps/src/handlers/content.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -22,14 +22,12 @@ use hyper::StatusCode; use parity_version::version; use handlers::add_security_headers; -use Embeddable; #[derive(Debug, Clone)] pub struct ContentHandler { code: StatusCode, content: String, mimetype: mime::Mime, - safe_to_embed_on: Embeddable, } impl ContentHandler { @@ -37,8 +35,8 @@ impl ContentHandler { Self::new(StatusCode::Ok, content, mimetype) } - pub fn html(code: StatusCode, content: String, embeddable_on: Embeddable) -> Self { - Self::new_embeddable(code, content, mime::TEXT_HTML, embeddable_on) + pub fn html(code: StatusCode, content: String) -> Self { + Self::new(code, content, mime::TEXT_HTML) } pub fn error( @@ -46,7 +44,6 @@ impl ContentHandler { title: &str, message: &str, details: Option<&str>, - embeddable_on: Embeddable, ) -> Self { Self::html(code, format!( include_str!("../error_tpl.html"), @@ -54,24 +51,18 @@ impl ContentHandler { message=message, details=details.unwrap_or_else(|| ""), version=version(), - ), embeddable_on) + )) } - pub fn new(code: StatusCode, content: String, mimetype: mime::Mime) -> Self { - Self::new_embeddable(code, content, mimetype, None) - } - - pub fn new_embeddable( + pub fn new( code: StatusCode, content: String, mimetype: mime::Mime, - safe_to_embed_on: Embeddable, ) -> Self { ContentHandler { code, content, mimetype, - safe_to_embed_on, } } } @@ -82,7 +73,7 @@ impl Into for ContentHandler { .with_status(self.code) .with_header(header::ContentType(self.mimetype)) .with_body(self.content); - add_security_headers(&mut res.headers_mut(), self.safe_to_embed_on, false); + add_security_headers(&mut res.headers_mut(), false); res } } diff --git a/dapps/src/handlers/echo.rs b/dapps/src/handlers/echo.rs index 375f047906fea1e065743adfa8440a11a7c97a04..03dfd1c974c1a3cc1276fe4b3c965ce688b6c71a 100644 --- a/dapps/src/handlers/echo.rs +++ b/dapps/src/handlers/echo.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -40,7 +40,7 @@ impl Into for EchoHandler { .with_header(content_type.unwrap_or(header::ContentType::json())) .with_body(self.request.body()); - add_security_headers(res.headers_mut(), None, false); + add_security_headers(res.headers_mut(), false); res } } diff --git a/dapps/src/handlers/errors.rs b/dapps/src/handlers/errors.rs new file mode 100644 index 0000000000000000000000000000000000000000..5261dc3c1585d2ca254b7c642d9fe665129072c5 --- /dev/null +++ b/dapps/src/handlers/errors.rs @@ -0,0 +1,66 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Handler errors. + +use handlers::{ContentHandler, FETCH_TIMEOUT}; +use hyper::StatusCode; +use std::fmt; + +pub fn streaming() -> ContentHandler { + ContentHandler::error( + StatusCode::BadGateway, + "Streaming Error", + "This content is being streamed in other place.", + None, + ) +} + +pub fn download_error(e: E) -> ContentHandler { + ContentHandler::error( + StatusCode::BadGateway, + "Download Error", + "There was an error when fetching the content.", + Some(&format!("{:?}", e)), + ) +} + +pub fn invalid_content(e: E) -> ContentHandler { + ContentHandler::error( + StatusCode::BadGateway, + "Invalid Dapp", + "Downloaded bundle does not contain a valid content.", + Some(&format!("{:?}", e)), + ) +} + +pub fn timeout_error() -> ContentHandler { + ContentHandler::error( + StatusCode::GatewayTimeout, + "Download Timeout", + &format!("Could not fetch content within {} seconds.", FETCH_TIMEOUT.as_secs()), + None, + ) +} + +pub fn method_not_allowed() -> ContentHandler { + ContentHandler::error( + StatusCode::MethodNotAllowed, + "Method Not Allowed", + "Only GET requests are allowed.", + None, + ) +} diff --git a/dapps/src/handlers/fetch.rs b/dapps/src/handlers/fetch.rs index 179bb4884c7e9f27ee319c6ced1fd6c1818a1f75..3fee3b1fec13f80e90ca8d62494fa5dc89edd829 100644 --- a/dapps/src/handlers/fetch.rs +++ b/dapps/src/handlers/fetch.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -19,20 +19,17 @@ use std::{fmt, mem}; use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; -use std::time::{Instant, Duration}; +use std::time::Instant; use fetch::{self, Fetch}; use futures::sync::oneshot; use futures::{self, Future}; use futures_cpupool::CpuPool; -use hyper::{self, Method, StatusCode}; +use hyper; use parking_lot::Mutex; use endpoint::{self, EndpointPath}; -use handlers::{ContentHandler, StreamingHandler}; +use handlers::{ContentHandler, StreamingHandler, FETCH_TIMEOUT, errors}; use page::local; -use {Embeddable}; - -const FETCH_TIMEOUT: u64 = 300; pub enum ValidatorResponse { Local(local::Dapp), @@ -57,7 +54,7 @@ impl Default for FetchControl { FetchControl { abort: Arc::new(AtomicBool::new(false)), listeners: Arc::new(Mutex::new(Vec::new())), - deadline: Instant::now() + Duration::from_secs(FETCH_TIMEOUT), + deadline: Instant::now() + FETCH_TIMEOUT, } } } @@ -102,7 +99,6 @@ impl FetchControl { } } - enum WaitState { Waiting(oneshot::Receiver), Done(endpoint::Response), @@ -135,8 +131,7 @@ impl Future for WaitingHandler { return Ok(futures::Async::Ready(handler.into())); }, WaitResult::NonAwaitable => { - let errors = Errors { embeddable_on: None }; - return Ok(futures::Async::Ready(errors.streaming().into())); + return Ok(futures::Async::Ready(errors::streaming().into())); }, WaitResult::Done(endpoint) => { WaitState::Done(endpoint.to_response(&self.path).into()) @@ -153,63 +148,6 @@ impl Future for WaitingHandler { } } -#[derive(Debug, Clone)] -struct Errors { - embeddable_on: Embeddable, -} - -impl Errors { - fn streaming(&self) -> ContentHandler { - ContentHandler::error( - StatusCode::BadGateway, - "Streaming Error", - "This content is being streamed in other place.", - None, - self.embeddable_on.clone(), - ) - } - - fn download_error(&self, e: E) -> ContentHandler { - ContentHandler::error( - StatusCode::BadGateway, - "Download Error", - "There was an error when fetching the content.", - Some(&format!("{:?}", e)), - self.embeddable_on.clone(), - ) - } - - fn invalid_content(&self, e: E) -> ContentHandler { - ContentHandler::error( - StatusCode::BadGateway, - "Invalid Dapp", - "Downloaded bundle does not contain a valid content.", - Some(&format!("{:?}", e)), - self.embeddable_on.clone(), - ) - } - - fn timeout_error(&self) -> ContentHandler { - ContentHandler::error( - StatusCode::GatewayTimeout, - "Download Timeout", - &format!("Could not fetch content within {} seconds.", FETCH_TIMEOUT), - None, - self.embeddable_on.clone(), - ) - } - - fn method_not_allowed(&self) -> ContentHandler { - ContentHandler::error( - StatusCode::MethodNotAllowed, - "Method Not Allowed", - "Only GET requests are allowed.", - None, - self.embeddable_on.clone(), - ) - } -} - enum FetchState { Error(ContentHandler), InProgress(Box + Send>), @@ -238,7 +176,6 @@ impl fmt::Debug for FetchState { pub struct ContentFetcherHandler { fetch_control: FetchControl, status: FetchState, - errors: Errors, } impl ContentFetcherHandler { @@ -251,17 +188,15 @@ impl ContentFetcherHandler { url: &str, path: EndpointPath, installer: H, - embeddable_on: Embeddable, fetch: F, pool: CpuPool, ) -> Self { let fetch_control = FetchControl::default(); - let errors = Errors { embeddable_on }; // Validation of method let status = match *method { // Start fetching content - Method::Get => { + hyper::Method::Get => { trace!(target: "dapps", "Fetching content from: {:?}", url); FetchState::InProgress(Self::fetch_content( pool, @@ -269,18 +204,16 @@ impl ContentFetcherHandler { url, fetch_control.abort.clone(), path, - errors.clone(), installer, )) }, // or return error - _ => FetchState::Error(errors.method_not_allowed()), + _ => FetchState::Error(errors::method_not_allowed()), }; ContentFetcherHandler { fetch_control, status, - errors, } } @@ -290,12 +223,11 @@ impl ContentFetcherHandler { url: &str, abort: Arc, path: EndpointPath, - errors: Errors, installer: H, ) -> Box + Send> { // Start fetching the content let pool2 = pool.clone(); - let future = fetch.fetch(url, abort.into()).then(move |result| { + let future = fetch.get(url, abort.into()).then(move |result| { trace!(target: "dapps", "Fetching content finished. Starting validation: {:?}", result); Ok(match result { Ok(response) => match installer.validate_and_install(response) { @@ -312,12 +244,12 @@ impl ContentFetcherHandler { }, Err(e) => { trace!(target: "dapps", "Error while validating content: {:?}", e); - FetchState::Error(errors.invalid_content(e)) + FetchState::Error(errors::invalid_content(e)) }, }, Err(e) => { warn!(target: "dapps", "Unable to fetch content: {:?}", e); - FetchState::Error(errors.download_error(e)) + FetchState::Error(errors::download_error(e)) }, }) }); @@ -348,7 +280,7 @@ impl Future for ContentFetcherHandler { // Request may time out FetchState::InProgress(_) if self.fetch_control.is_deadline_reached() => { trace!(target: "dapps", "Fetching dapp failed because of timeout."); - FetchState::Error(self.errors.timeout_error()) + FetchState::Error(errors::timeout_error()) }, FetchState::InProgress(ref mut receiver) => { // Check if there is a response diff --git a/dapps/src/handlers/mod.rs b/dapps/src/handlers/mod.rs index f78f46c76471d0e11236290c44f723af41aef6f8..cb0eba042936ea4b95473f86548ee5ab865f876d 100644 --- a/dapps/src/handlers/mod.rs +++ b/dapps/src/handlers/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -22,6 +22,7 @@ mod fetch; mod reader; mod redirect; mod streaming; +mod errors; pub use self::content::ContentHandler; pub use self::echo::EchoHandler; @@ -30,20 +31,16 @@ pub use self::reader::Reader; pub use self::redirect::Redirection; pub use self::streaming::StreamingHandler; -use std::iter; -use itertools::Itertools; use hyper::header; -use {apps, address, Embeddable}; +use std::time::Duration; + +const FETCH_TIMEOUT: Duration = Duration::from_secs(300); /// Adds security-related headers to the Response. -pub fn add_security_headers(headers: &mut header::Headers, embeddable_on: Embeddable, allow_js_eval: bool) { +pub fn add_security_headers(headers: &mut header::Headers, allow_js_eval: bool) { headers.set_raw("X-XSS-Protection", "1; mode=block"); headers.set_raw("X-Content-Type-Options", "nosniff"); - - // Embedding header: - if let None = embeddable_on { - headers.set_raw("X-Frame-Options", "SAMEORIGIN"); - } + headers.set_raw("X-Frame-Options", "SAMEORIGIN"); // Content Security Policy headers headers.set_raw("Content-Security-Policy", String::new() @@ -70,11 +67,7 @@ pub fn add_security_headers(headers: &mut header::Headers, embeddable_on: Embedd + "object-src 'none';" // Allow scripts + { - let script_src = embeddable_on.as_ref() - .map(|e| e.extra_script_src.iter() - .map(|&(ref host, port)| address(host, port)) - .join(" ") - ).unwrap_or_default(); + let script_src = ""; let eval = if allow_js_eval { " 'unsafe-eval'" } else { "" }; &format!( @@ -93,29 +86,6 @@ pub fn add_security_headers(headers: &mut header::Headers, embeddable_on: Embedd // Never allow mixed content + "block-all-mixed-content;" // Specify if the site can be embedded. - + &match embeddable_on { - Some(ref embed) => { - let std = address(&embed.host, embed.port); - let proxy = format!("{}.{}", apps::HOME_PAGE, embed.dapps_domain); - let domain = format!("*.{}:{}", embed.dapps_domain, embed.port); - - let mut ancestors = vec![std, domain, proxy] - .into_iter() - .chain(embed.extra_embed_on - .iter() - .map(|&(ref host, port)| address(host, port)) - ); - - let ancestors = if embed.host == "127.0.0.1" { - let localhost = address("localhost", embed.port); - ancestors.chain(iter::once(localhost)).join(" ") - } else { - ancestors.join(" ") - }; - - format!("frame-ancestors {};", ancestors) - }, - None => format!("frame-ancestors 'self';"), - } + + "frame-ancestors 'self';" ); } diff --git a/dapps/src/handlers/reader.rs b/dapps/src/handlers/reader.rs index 85a351c7b0c934f832d33cc038a3aa1351f44f0d..3b0aa5449b239ea870b6dff2e5c22de013bf4d99 100644 --- a/dapps/src/handlers/reader.rs +++ b/dapps/src/handlers/reader.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/dapps/src/handlers/redirect.rs b/dapps/src/handlers/redirect.rs index cb1eda2dd555706effa224f83af19916adf99b94..c8bf837d8566e358140e09e85b80c625168d59c1 100644 --- a/dapps/src/handlers/redirect.rs +++ b/dapps/src/handlers/redirect.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/dapps/src/handlers/streaming.rs b/dapps/src/handlers/streaming.rs index 269e4c5d2a730a57d4a024543857ac23b5b2901f..b6feaa6382735dc2c8594a9a4ff27de98543147c 100644 --- a/dapps/src/handlers/streaming.rs +++ b/dapps/src/handlers/streaming.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -20,24 +20,21 @@ use std::io; use hyper::{self, header, mime, StatusCode}; use handlers::{add_security_headers, Reader}; -use Embeddable; pub struct StreamingHandler { initial: Vec, content: R, status: StatusCode, mimetype: mime::Mime, - safe_to_embed_on: Embeddable, } impl StreamingHandler { - pub fn new(content: R, status: StatusCode, mimetype: mime::Mime, safe_to_embed_on: Embeddable) -> Self { + pub fn new(content: R, status: StatusCode, mimetype: mime::Mime) -> Self { StreamingHandler { initial: Vec::new(), content, status, mimetype, - safe_to_embed_on, } } @@ -51,7 +48,7 @@ impl StreamingHandler { .with_status(self.status) .with_header(header::ContentType(self.mimetype)) .with_body(body); - add_security_headers(&mut res.headers_mut(), self.safe_to_embed_on, false); + add_security_headers(&mut res.headers_mut(), false); (reader, res) } diff --git a/dapps/src/lib.rs b/dapps/src/lib.rs index 482ee3959580f2a91cffc20c0817feedd28ad57a..12a6a805086816e84984f04a47356af7703677ad 100644 --- a/dapps/src/lib.rs +++ b/dapps/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -38,7 +38,6 @@ extern crate fetch; extern crate node_health; extern crate parity_dapps_glue as parity_dapps; extern crate parity_hash_fetch as hash_fetch; -extern crate parity_ui; extern crate keccak_hash as hash; extern crate parity_version; extern crate registrar; @@ -83,7 +82,7 @@ use node_health::NodeHealth; pub use registrar::{RegistrarClient, Asynchronous}; pub use node_health::SyncStatus; - +pub use page::builtin::Dapp; /// Validates Web Proxy tokens pub trait WebProxyTokens: Send + Sync { @@ -101,7 +100,6 @@ pub struct Endpoints { local_endpoints: Arc>>, endpoints: Arc>, dapps_path: PathBuf, - embeddable: Option, pool: Option, } @@ -119,7 +117,7 @@ impl Endpoints { None => return, Some(pool) => pool, }; - let new_local = apps::fs::local_endpoints(&self.dapps_path, self.embeddable.clone(), pool.clone()); + let new_local = apps::fs::local_endpoints(&self.dapps_path, pool.clone()); let old_local = mem::replace(&mut *self.local_endpoints.write(), new_local.keys().cloned().collect()); let (_, to_remove): (_, Vec<_>) = old_local .into_iter() @@ -151,51 +149,10 @@ impl Middleware { &self.endpoints } - /// Creates new middleware for UI server. - pub fn ui( - pool: CpuPool, - health: NodeHealth, - dapps_domain: &str, - registrar: Arc>, - sync_status: Arc, - fetch: F, - ) -> Self { - let content_fetcher = Arc::new(apps::fetcher::ContentFetcher::new( - hash_fetch::urlhint::URLHintContract::new(registrar), - sync_status.clone(), - fetch.clone(), - pool.clone(), - ).embeddable_on(None).allow_dapps(false)); - let special = { - let mut special = special_endpoints( - pool.clone(), - health, - content_fetcher.clone(), - ); - special.insert(router::SpecialEndpoint::Home, Some(apps::ui(pool.clone()))); - special - }; - let router = router::Router::new( - content_fetcher, - None, - special, - None, - dapps_domain.to_owned(), - ); - - Middleware { - endpoints: Default::default(), - router: router, - } - } - /// Creates new Dapps server middleware. pub fn dapps( pool: CpuPool, health: NodeHealth, - ui_address: Option<(String, u16)>, - extra_embed_on: Vec<(String, u16)>, - extra_script_src: Vec<(String, u16)>, dapps_path: PathBuf, extra_dapps: Vec, dapps_domain: &str, @@ -204,18 +161,16 @@ impl Middleware { web_proxy_tokens: Arc, fetch: F, ) -> Self { - let embeddable = as_embeddable(ui_address, extra_embed_on, extra_script_src, dapps_domain); let content_fetcher = Arc::new(apps::fetcher::ContentFetcher::new( hash_fetch::urlhint::URLHintContract::new(registrar), sync_status.clone(), fetch.clone(), pool.clone(), - ).embeddable_on(embeddable.clone()).allow_dapps(true)); + ).allow_dapps(true)); let (local_endpoints, endpoints) = apps::all_endpoints( dapps_path.clone(), extra_dapps, dapps_domain, - embeddable.clone(), web_proxy_tokens, fetch.clone(), pool.clone(), @@ -224,28 +179,18 @@ impl Middleware { endpoints: Arc::new(RwLock::new(endpoints)), dapps_path, local_endpoints: Arc::new(RwLock::new(local_endpoints)), - embeddable: embeddable.clone(), pool: Some(pool.clone()), }; - let special = { - let mut special = special_endpoints( - pool.clone(), - health, - content_fetcher.clone(), - ); - special.insert( - router::SpecialEndpoint::Home, - Some(apps::ui_redirection(embeddable.clone())), - ); - special - }; + let special = special_endpoints( + health, + content_fetcher.clone(), + ); let router = router::Router::new( content_fetcher, Some(endpoints.clone()), special, - embeddable, dapps_domain.to_owned(), ); @@ -263,13 +208,11 @@ impl http::RequestMiddleware for Middleware { } fn special_endpoints( - pool: CpuPool, health: NodeHealth, content_fetcher: Arc, ) -> HashMap>> { let mut special = HashMap::new(); special.insert(router::SpecialEndpoint::Rpc, None); - special.insert(router::SpecialEndpoint::Utils, Some(apps::utils(pool))); special.insert(router::SpecialEndpoint::Api, Some(api::RestApi::new( content_fetcher, health, @@ -277,45 +220,9 @@ fn special_endpoints( special } -fn address(host: &str, port: u16) -> String { - format!("{}:{}", host, port) -} - -fn as_embeddable( - ui_address: Option<(String, u16)>, - extra_embed_on: Vec<(String, u16)>, - extra_script_src: Vec<(String, u16)>, - dapps_domain: &str, -) -> Option { - ui_address.map(|(host, port)| ParentFrameSettings { - host, - port, - extra_embed_on, - extra_script_src, - dapps_domain: dapps_domain.to_owned(), - }) -} - /// Random filename fn random_filename() -> String { use ::rand::Rng; let mut rng = ::rand::OsRng::new().unwrap(); rng.gen_ascii_chars().take(12).collect() } - -type Embeddable = Option; - -/// Parent frame host and port allowed to embed the content. -#[derive(Debug, Clone)] -pub struct ParentFrameSettings { - /// Hostname - pub host: String, - /// Port - pub port: u16, - /// Additional URLs the dapps can be embedded on. - pub extra_embed_on: Vec<(String, u16)>, - /// Additional URLs the dapp scripts can be loaded from. - pub extra_script_src: Vec<(String, u16)>, - /// Dapps Domain (web3.site) - pub dapps_domain: String, -} diff --git a/dapps/src/page/builtin.rs b/dapps/src/page/builtin.rs index 827fe27a3b4e15e09151beec80ffc1261f0fbab1..685b1401d15b9c5999dea0c9b1508727de907118 100644 --- a/dapps/src/page/builtin.rs +++ b/dapps/src/page/builtin.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -23,15 +23,13 @@ use parity_dapps::{WebApp, Info}; use endpoint::{Endpoint, EndpointInfo, EndpointPath, Request, Response}; use page::{handler, PageCache}; -use Embeddable; +/// Represents a builtin Dapp. pub struct Dapp { /// futures cpu pool pool: CpuPool, /// Content of the files app: T, - /// Safe to be loaded in frame by other origin. (use wisely!) - safe_to_embed_on: Embeddable, info: EndpointInfo, fallback_to_index_html: bool, } @@ -43,7 +41,6 @@ impl Dapp { Dapp { pool, app, - safe_to_embed_on: None, info: EndpointInfo::from(info), fallback_to_index_html: false, } @@ -56,24 +53,14 @@ impl Dapp { Dapp { pool, app, - safe_to_embed_on: None, info: EndpointInfo::from(info), fallback_to_index_html: true, } } - /// Creates new `Dapp` which can be safely used in iframe - /// even from different origin. It might be dangerous (clickjacking). - /// Use wisely! - pub fn new_safe_to_embed(pool: CpuPool, app: T, address: Embeddable) -> Self { - let info = app.info(); - Dapp { - pool, - app, - safe_to_embed_on: address, - info: EndpointInfo::from(info), - fallback_to_index_html: false, - } + /// Allow the dapp to use `unsafe-eval` to run JS. + pub fn allow_js_eval(&mut self) { + self.info.allow_js_eval = Some(true); } } @@ -116,7 +103,6 @@ impl Endpoint for Dapp { let (reader, response) = handler::PageHandler { file, cache: PageCache::Disabled, - safe_to_embed_on: self.safe_to_embed_on.clone(), allow_js_eval: self.info.allow_js_eval.clone().unwrap_or(false), }.into_response(); @@ -141,7 +127,6 @@ impl From for EndpointInfo { } } - struct BuiltinFile { content_type: Mime, content: io::Cursor<&'static [u8]>, diff --git a/dapps/src/page/handler.rs b/dapps/src/page/handler.rs index a4bd6d71b3eba02878d9153a9d4040a629517b91..d7fcefa7f0428cd8a768b77574f379756e7afff3 100644 --- a/dapps/src/page/handler.rs +++ b/dapps/src/page/handler.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -17,11 +17,9 @@ use std::io; use std::time::{Duration, SystemTime}; use hyper::{self, header, StatusCode}; -use hyper::mime::{self, Mime}; +use hyper::mime::{Mime}; -use apps; use handlers::{Reader, ContentHandler, add_security_headers}; -use {Embeddable}; /// Represents a file that can be sent to client. /// Implementation should keep track of bytes already sent internally. @@ -55,8 +53,6 @@ impl Default for PageCache { pub struct PageHandler { /// File currently being served pub file: Option, - /// Flag indicating if the file can be safely embeded (put in iframe). - pub safe_to_embed_on: Embeddable, /// Cache settings for this page. pub cache: PageCache, /// Allow JS unsafe-eval. @@ -71,7 +67,6 @@ impl PageHandler { "File not found", "Requested file has not been found.", None, - self.safe_to_embed_on, ).into()), Some(file) => file, }; @@ -95,21 +90,10 @@ impl PageHandler { headers.set(header::ContentType(file.content_type().to_owned())); - add_security_headers(&mut headers, self.safe_to_embed_on, self.allow_js_eval); + add_security_headers(&mut headers, self.allow_js_eval); } - let initial_content = if file.content_type().to_owned() == mime::TEXT_HTML { - let content = &format!( - r#""#, - apps::UTILS_PATH, - ); - - content.as_bytes().to_vec() - } else { - Vec::new() - }; - - let (reader, body) = Reader::pair(file.into_reader(), initial_content); + let (reader, body) = Reader::pair(file.into_reader(), Vec::new()); res.set_body(body); (Some(reader), res) } diff --git a/dapps/src/page/local.rs b/dapps/src/page/local.rs index a1746efcd2172f5f82ee896a8bccffc2d2ef99d3..a43735d7684338cc954b1c9e1cece6762c5dc33c 100644 --- a/dapps/src/page/local.rs +++ b/dapps/src/page/local.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -22,7 +22,6 @@ use futures_cpupool::CpuPool; use page::handler::{self, PageCache}; use endpoint::{Endpoint, EndpointInfo, EndpointPath, Request, Response}; use hyper::mime::Mime; -use Embeddable; #[derive(Clone)] pub struct Dapp { @@ -31,7 +30,6 @@ pub struct Dapp { mime: Option, info: Option, cache: PageCache, - embeddable_on: Embeddable, } impl fmt::Debug for Dapp { @@ -41,20 +39,18 @@ impl fmt::Debug for Dapp { .field("mime", &self.mime) .field("info", &self.info) .field("cache", &self.cache) - .field("embeddable_on", &self.embeddable_on) .finish() } } impl Dapp { - pub fn new(pool: CpuPool, path: PathBuf, info: EndpointInfo, cache: PageCache, embeddable_on: Embeddable) -> Self { + pub fn new(pool: CpuPool, path: PathBuf, info: EndpointInfo, cache: PageCache) -> Self { Dapp { pool, path, mime: None, info: Some(info), cache, - embeddable_on, } } @@ -65,7 +61,6 @@ impl Dapp { mime: Some(mime), info: None, cache, - embeddable_on: None, } } @@ -92,12 +87,10 @@ impl Dapp { LocalFile::from_path(&file_path, mime) } - pub fn to_response(&self, path: &EndpointPath) -> Response { let (reader, response) = handler::PageHandler { file: self.get_file(path), cache: self.cache, - safe_to_embed_on: self.embeddable_on.clone(), allow_js_eval: self.info.as_ref().and_then(|x| x.allow_js_eval).unwrap_or(false), }.into_response(); diff --git a/dapps/src/page/mod.rs b/dapps/src/page/mod.rs index 420707bfe1955513d59dd8299c72d3638b1345c4..65385320c4809bea07874459955ca0458664c918 100644 --- a/dapps/src/page/mod.rs +++ b/dapps/src/page/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -14,10 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . - pub mod builtin; pub mod local; mod handler; pub use self::handler::PageCache; - diff --git a/dapps/src/proxypac.rs b/dapps/src/proxypac.rs index 85ac11423a11ed4525ab80f2e19af88c184c8da9..1acd7b1b9e39d0aad64232a4840575fa0e5317bb 100644 --- a/dapps/src/proxypac.rs +++ b/dapps/src/proxypac.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -21,25 +21,20 @@ use endpoint::{Endpoint, Request, Response, EndpointPath}; use futures::future; use handlers::ContentHandler; use hyper::mime; -use {address, Embeddable}; pub struct ProxyPac { - embeddable: Embeddable, dapps_domain: String, } impl ProxyPac { - pub fn boxed(embeddable: Embeddable, dapps_domain: String) -> Box { - Box::new(ProxyPac { embeddable, dapps_domain }) + pub fn boxed(dapps_domain: String) -> Box { + Box::new(ProxyPac { dapps_domain }) } } impl Endpoint for ProxyPac { fn respond(&self, path: EndpointPath, _req: Request) -> Response { - let ui = self.embeddable - .as_ref() - .map(|ref parent| address(&parent.host, parent.port)) - .unwrap_or_else(|| format!("{}:{}", path.host, path.port)); + let ui = format!("{}:{}", path.host, path.port); let content = format!( r#" @@ -64,5 +59,3 @@ function FindProxyForURL(url, host) {{ )) } } - - diff --git a/dapps/src/router.rs b/dapps/src/router.rs index e5770ca72b8f77578d21cdf646499da8930e3af2..28a3e24c3f0ab5b18af38f643a3ec1f1306c1dc0 100644 --- a/dapps/src/router.rs +++ b/dapps/src/router.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -29,14 +29,12 @@ use apps::fetcher::Fetcher; use endpoint::{self, Endpoint, EndpointPath}; use Endpoints; use handlers; -use Embeddable; /// Special endpoints are accessible on every domain (every dapp) #[derive(Debug, PartialEq, Hash, Eq)] pub enum SpecialEndpoint { Rpc, Api, - Utils, Home, None, } @@ -52,16 +50,14 @@ pub struct Router { endpoints: Option, fetch: Arc, special: HashMap>>, - embeddable_on: Embeddable, dapps_domain: String, } impl Router { - fn resolve_request(&self, req: hyper::Request, refresh_dapps: bool) -> (bool, Response) { + fn resolve_request(&self, req: hyper::Request, refresh_dapps: bool) -> Response { // Choose proper handler depending on path / domain let endpoint = extract_endpoint(req.uri(), req.headers().get(), &self.dapps_domain); let referer = extract_referer_endpoint(&req, &self.dapps_domain); - let is_utils = endpoint.1 == SpecialEndpoint::Utils; let is_get_request = *req.method() == hyper::Method::Get; let is_head_request = *req.method() == hyper::Method::Head; let has_dapp = |dapp: &str| self.endpoints @@ -71,7 +67,7 @@ impl Router { trace!(target: "dapps", "Routing request to {:?}. Details: {:?}", req.uri(), req); debug!(target: "dapps", "Handling endpoint request: {:?}, referer: {:?}", endpoint, referer); - (is_utils, match (endpoint.0, endpoint.1, referer) { + match (endpoint.0, endpoint.1, referer) { // Handle invalid web requests that we can recover from (ref path, SpecialEndpoint::None, Some(ref referer)) if referer.app_id == apps::WEB_PATH @@ -132,7 +128,6 @@ impl Router { "404 Not Found", "Requested content was not found.", None, - self.embeddable_on.clone(), ).into()))) } }, @@ -150,21 +145,30 @@ impl Router { } }, // RPC by default - _ => { + _ if self.special.contains_key(&SpecialEndpoint::Rpc) => { trace!(target: "dapps", "Resolving to RPC call."); Response::None(req) - } - }) + }, + // 404 otherwise + _ => { + Response::Some(Box::new(future::ok(handlers::ContentHandler::error( + hyper::StatusCode::NotFound, + "404 Not Found", + "Requested content was not found.", + None, + ).into()))) + }, + } } } impl http::RequestMiddleware for Router { fn on_request(&self, req: hyper::Request) -> http::RequestMiddlewareAction { let is_origin_set = req.headers().get::().is_some(); - let (is_utils, response) = self.resolve_request(req, self.endpoints.is_some()); + let response = self.resolve_request(req, self.endpoints.is_some()); match response { Response::Some(response) => http::RequestMiddlewareAction::Respond { - should_validate_hosts: !is_utils, + should_validate_hosts: true, response, }, Response::None(request) => http::RequestMiddlewareAction::Proceed { @@ -180,14 +184,12 @@ impl Router { content_fetcher: Arc, endpoints: Option, special: HashMap>>, - embeddable_on: Embeddable, dapps_domain: String, ) -> Self { Router { endpoints: endpoints, fetch: content_fetcher, special: special, - embeddable_on: embeddable_on, dapps_domain: format!(".{}", dapps_domain), } } @@ -240,7 +242,6 @@ fn extract_endpoint(url: &Uri, extra_host: Option<&header::Host>, dapps_domain: match path[0].as_ref() { apps::RPC_PATH => SpecialEndpoint::Rpc, apps::API_PATH => SpecialEndpoint::Api, - apps::UTILS_PATH => SpecialEndpoint::Utils, apps::HOME_PAGE => SpecialEndpoint::Home, _ => SpecialEndpoint::None, } @@ -341,30 +342,6 @@ mod tests { }), SpecialEndpoint::Rpc) ); - assert_eq!( - extract_endpoint(&"http://my.status.web3.site/parity-utils/inject.js".parse().unwrap(), None, dapps_domain), - (Some(EndpointPath { - app_id: "status".to_owned(), - app_params: vec!["my".into(), "inject.js".into()], - query: None, - host: "my.status.web3.site".to_owned(), - port: 80, - using_dapps_domains: true, - }), SpecialEndpoint::Utils) - ); - - assert_eq!( - extract_endpoint(&"http://my.status.web3.site/inject.js".parse().unwrap(), None, dapps_domain), - (Some(EndpointPath { - app_id: "status".to_owned(), - app_params: vec!["my".into(), "inject.js".into()], - query: None, - host: "my.status.web3.site".to_owned(), - port: 80, - using_dapps_domains: true, - }), SpecialEndpoint::None) - ); - // By Subdomain assert_eq!( extract_endpoint(&"http://status.web3.site/test.html".parse().unwrap(), None, dapps_domain), diff --git a/dapps/src/tests/api.rs b/dapps/src/tests/api.rs index 3ae3f7cbbf88da4febf11e689c13f2ecb845d6cc..d31f796d57bfd2f4cb57ebbb36c88decd5f67682 100644 --- a/dapps/src/tests/api.rs +++ b/dapps/src/tests/api.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -63,7 +63,6 @@ fn should_handle_ping() { assert_security_headers(&response.headers); } - #[test] fn should_try_to_resolve_dapp() { // given diff --git a/dapps/src/tests/fetch.rs b/dapps/src/tests/fetch.rs index 59eeaf8d662e6fcef7df2a3d3197845609dac380..444d5b656acd8874036421bb1912067dcd8fadd4 100644 --- a/dapps/src/tests/fetch.rs +++ b/dapps/src/tests/fetch.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -19,7 +19,7 @@ use rustc_hex::FromHex; use tests::helpers::{ serve_with_registrar, serve_with_registrar_and_sync, serve_with_fetch, serve_with_registrar_and_fetch, - request, assert_security_headers_for_embed, + request, assert_security_headers }; #[test] @@ -40,7 +40,7 @@ fn should_resolve_dapp() { // then response.assert_status("HTTP/1.1 404 Not Found"); assert_eq!(registrar.calls.lock().len(), 4); - assert_security_headers_for_embed(&response.headers); + assert_security_headers(&response.headers); } #[test] @@ -61,7 +61,7 @@ fn should_return_503_when_syncing_but_should_make_the_calls() { // then response.assert_status("HTTP/1.1 503 Service Unavailable"); assert_eq!(registrar.calls.lock().len(), 2); - assert_security_headers_for_embed(&response.headers); + assert_security_headers(&response.headers); } const GAVCOIN_DAPP: &'static str = "00000000000000000000000000000000000000000000000000000000000000609faf32e1e3845e237cc6efd27187cee13b3b99db000000000000000000000000000000000000000000000000d8bd350823e28ff75e74a34215faefdc8a52fd8e00000000000000000000000000000000000000000000000000000000000000116761766f66796f726b2f676176636f696e000000000000000000000000000000"; @@ -95,7 +95,7 @@ fn should_return_502_on_hash_mismatch() { response.assert_status("HTTP/1.1 502 Bad Gateway"); assert!(response.body.contains("HashMismatch"), "Expected hash mismatch response, got: {:?}", response.body); - assert_security_headers_for_embed(&response.headers); + assert_security_headers(&response.headers); } #[test] @@ -126,7 +126,7 @@ fn should_return_error_for_invalid_dapp_zip() { response.assert_status("HTTP/1.1 502 Bad Gateway"); assert!(response.body.contains("InvalidArchive"), "Expected invalid zip response, got: {:?}", response.body); - assert_security_headers_for_embed(&response.headers); + assert_security_headers(&response.headers); } #[test] @@ -165,7 +165,7 @@ fn should_return_fetched_dapp_content() { fetch.assert_no_more_requests(); response1.assert_status("HTTP/1.1 200 OK"); - assert_security_headers_for_embed(&response1.headers); + assert_security_headers(&response1.headers); assert!( response1.body.contains(r#"18

Hello Gavcoin!

@@ -178,7 +178,7 @@ fn should_return_fetched_dapp_content() { ); response2.assert_status("HTTP/1.1 200 OK"); - assert_security_headers_for_embed(&response2.headers); + assert_security_headers(&response2.headers); assert_eq!( response2.body, r#"EA @@ -331,7 +331,7 @@ fn should_stream_web_content() { // then response.assert_status("HTTP/1.1 200 OK"); - assert_security_headers_for_embed(&response.headers); + assert_security_headers(&response.headers); fetch.assert_requested("https://parity.io/"); fetch.assert_no_more_requests(); @@ -354,7 +354,7 @@ fn should_support_base32_encoded_web_urls() { // then response.assert_status("HTTP/1.1 200 OK"); - assert_security_headers_for_embed(&response.headers); + assert_security_headers(&response.headers); fetch.assert_requested("https://parity.io/styles.css?test=123"); fetch.assert_no_more_requests(); @@ -377,13 +377,12 @@ fn should_correctly_handle_long_label_when_splitted() { // then response.assert_status("HTTP/1.1 200 OK"); - assert_security_headers_for_embed(&response.headers); + assert_security_headers(&response.headers); fetch.assert_requested("https://contribution.melonport.com/styles.css?test=123"); fetch.assert_no_more_requests(); } - #[test] fn should_support_base32_encoded_web_urls_as_path() { // given @@ -401,7 +400,7 @@ fn should_support_base32_encoded_web_urls_as_path() { // then response.assert_status("HTTP/1.1 200 OK"); - assert_security_headers_for_embed(&response.headers); + assert_security_headers(&response.headers); fetch.assert_requested("https://parity.io/styles.css?test=123"); fetch.assert_no_more_requests(); @@ -424,7 +423,7 @@ fn should_return_error_on_non_whitelisted_domain() { // then response.assert_status("HTTP/1.1 400 Bad Request"); - assert_security_headers_for_embed(&response.headers); + assert_security_headers(&response.headers); fetch.assert_no_more_requests(); } @@ -446,7 +445,7 @@ fn should_return_error_on_invalid_token() { // then response.assert_status("HTTP/1.1 400 Bad Request"); - assert_security_headers_for_embed(&response.headers); + assert_security_headers(&response.headers); fetch.assert_no_more_requests(); } @@ -468,7 +467,7 @@ fn should_return_error_on_invalid_protocol() { // then response.assert_status("HTTP/1.1 400 Bad Request"); - assert_security_headers_for_embed(&response.headers); + assert_security_headers(&response.headers); fetch.assert_no_more_requests(); } @@ -493,7 +492,7 @@ fn should_disallow_non_get_requests() { // then response.assert_status("HTTP/1.1 405 Method Not Allowed"); - assert_security_headers_for_embed(&response.headers); + assert_security_headers(&response.headers); fetch.assert_no_more_requests(); } diff --git a/dapps/src/tests/helpers/fetch.rs b/dapps/src/tests/helpers/fetch.rs index dfe523200e45e06005b49cc47c95b5af874a57c0..4affffe6efa2b4f5d835b81ab38166270940a615 100644 --- a/dapps/src/tests/helpers/fetch.rs +++ b/dapps/src/tests/helpers/fetch.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -19,8 +19,8 @@ use std::sync::{atomic, mpsc, Arc}; use parking_lot::Mutex; use hyper; -use futures::{self, Future}; -use fetch::{self, Fetch, Url}; +use futures::{self, future, Future}; +use fetch::{self, Fetch, Url, Request, Abort}; pub struct FetchControl { sender: mpsc::Sender<()>, @@ -34,8 +34,8 @@ impl FetchControl { } pub fn wait_for_requests(&self, len: usize) { - const MAX_TIMEOUT_MS: u64 = 5000; - const ATTEMPTS: u64 = 10; + const MAX_TIMEOUT: time::Duration = time::Duration::from_millis(5000); + const ATTEMPTS: u32 = 10; let mut attempts_left = ATTEMPTS; loop { let current = self.fetch.requested.lock().len(); @@ -50,7 +50,7 @@ impl FetchControl { } else { attempts_left -= 1; // Should we handle spurious timeouts better? - thread::park_timeout(time::Duration::from_millis(MAX_TIMEOUT_MS / ATTEMPTS)); + thread::park_timeout(MAX_TIMEOUT / ATTEMPTS); } } } @@ -97,9 +97,9 @@ impl FakeFetch { impl Fetch for FakeFetch { type Result = Box + Send>; - fn fetch(&self, url: &str, abort: fetch::Abort) -> Self::Result { - let u = Url::parse(url).unwrap(); - self.requested.lock().push(url.into()); + fn fetch(&self, request: Request, abort: fetch::Abort) -> Self::Result { + let u = request.url().clone(); + self.requested.lock().push(u.as_str().into()); let manual = self.manual.clone(); let response = self.response.clone(); @@ -115,4 +115,20 @@ impl Fetch for FakeFetch { Box::new(rx.map_err(|_| fetch::Error::Aborted)) } + + fn get(&self, url: &str, abort: Abort) -> Self::Result { + let url: Url = match url.parse() { + Ok(u) => u, + Err(e) => return Box::new(future::err(e.into())) + }; + self.fetch(Request::get(url), abort) + } + + fn post(&self, url: &str, abort: Abort) -> Self::Result { + let url: Url = match url.parse() { + Ok(u) => u, + Err(e) => return Box::new(future::err(e.into())) + }; + self.fetch(Request::post(url), abort) + } } diff --git a/dapps/src/tests/helpers/mod.rs b/dapps/src/tests/helpers/mod.rs index 3a1311578c7c776c28e21c334a223981b7cf2d35..58ec71d7332db93b790e8fbde018e756aedb3ddf 100644 --- a/dapps/src/tests/helpers/mod.rs +++ b/dapps/src/tests/helpers/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -36,8 +36,6 @@ mod fetch; use self::registrar::FakeRegistrar; use self::fetch::FakeFetch; -const SIGNER_PORT: u16 = 18180; - #[derive(Debug)] struct FakeSync(bool); impl SyncStatus for FakeSync { @@ -63,8 +61,7 @@ pub fn init_server(process: F, io: IoHandler) -> (Server, Arc Server { init_server(|builder| builder, Default::default()).0 } -pub fn serve_ui() -> Server { - init_server(|mut builder| { - builder.serve_ui = true; - builder - }, Default::default()).0 -} - pub fn request(server: Server, request: &str) -> http_client::Response { http_client::request(server.addr(), request) } @@ -136,10 +126,6 @@ pub fn request(server: Server, request: &str) -> http_client::Response { pub fn assert_security_headers(headers: &[String]) { http_client::assert_security_headers_present(headers, None) } -pub fn assert_security_headers_for_embed(headers: &[String]) { - http_client::assert_security_headers_present(headers, Some(SIGNER_PORT)) -} - /// Webapps HTTP+RPC server build. pub struct ServerBuilder { @@ -147,10 +133,8 @@ pub struct ServerBuilder { registrar: Arc>, sync_status: Arc, web_proxy_tokens: Arc, - signer_address: Option<(String, u16)>, allowed_hosts: DomainsValidation, fetch: T, - serve_ui: bool, } impl ServerBuilder { @@ -161,10 +145,8 @@ impl ServerBuilder { registrar: registrar, sync_status: Arc::new(FakeSync(false)), web_proxy_tokens: Arc::new(|_| None), - signer_address: None, allowed_hosts: DomainsValidation::Disabled, fetch: fetch, - serve_ui: false, } } } @@ -177,10 +159,8 @@ impl ServerBuilder { registrar: self.registrar, sync_status: self.sync_status, web_proxy_tokens: self.web_proxy_tokens, - signer_address: self.signer_address, allowed_hosts: self.allowed_hosts, fetch: fetch, - serve_ui: self.serve_ui, } } @@ -191,7 +171,6 @@ impl ServerBuilder { addr, io, self.allowed_hosts, - self.signer_address, self.dapps_path, vec![], self.registrar, @@ -199,7 +178,6 @@ impl ServerBuilder { self.web_proxy_tokens, Remote::new_sync(), self.fetch, - self.serve_ui, ) } } @@ -216,7 +194,6 @@ impl Server { addr: &SocketAddr, io: IoHandler, allowed_hosts: DomainsValidation, - signer_address: Option<(String, u16)>, dapps_path: PathBuf, extra_dapps: Vec, registrar: Arc>, @@ -224,7 +201,6 @@ impl Server { web_proxy_tokens: Arc, remote: Remote, fetch: F, - serve_ui: bool, ) -> io::Result { let health = NodeHealth::new( sync_status.clone(), @@ -232,22 +208,10 @@ impl Server { remote.clone(), ); let pool = ::futures_cpupool::CpuPool::new(1); - let middleware = if serve_ui { - Middleware::ui( - pool, - health, - DAPPS_DOMAIN.into(), - registrar, - sync_status, - fetch, - ) - } else { + let middleware = Middleware::dapps( pool, health, - signer_address, - vec![], - vec![], dapps_path, extra_dapps, DAPPS_DOMAIN.into(), @@ -255,8 +219,7 @@ impl Server { sync_status, web_proxy_tokens, fetch, - ) - }; + ); let mut allowed_hosts: Option> = allowed_hosts.into(); allowed_hosts.as_mut().map(|hosts| { diff --git a/dapps/src/tests/helpers/registrar.rs b/dapps/src/tests/helpers/registrar.rs index 8668d43b131be00076ca0fb4e984cd69d1c22d09..b9acb1afc3e38e5f09d95a4a4b7fa6f6e3f1273e 100644 --- a/dapps/src/tests/helpers/registrar.rs +++ b/dapps/src/tests/helpers/registrar.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -55,7 +55,7 @@ impl FakeRegistrar { pub fn set_result(&self, hash: H256, result: Result) { self.responses.lock().insert( - (URLHINT.into(), format!("{}{:?}", URLHINT_RESOLVE, hash)), + (URLHINT.into(), format!("{}{:x}", URLHINT_RESOLVE, hash)), result ); } diff --git a/dapps/src/tests/home.rs b/dapps/src/tests/home.rs deleted file mode 100644 index 0ee0653648220ca5211078bd5af9039820fc129c..0000000000000000000000000000000000000000 --- a/dapps/src/tests/home.rs +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2015-2017 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 . - -use tests::helpers::{serve_ui, request, assert_security_headers}; - -#[test] -fn should_serve_home_js() { - // given - let server = serve_ui(); - - // when - let response = request(server, - "\ - GET /inject.js HTTP/1.1\r\n\ - Host: 127.0.0.1:8080\r\n\ - Connection: close\r\n\ - \r\n\ - {} - " - ); - - // then - response.assert_status("HTTP/1.1 200 OK"); - response.assert_header("Content-Type", "application/javascript"); - assert_eq!(response.body.contains("function(){"), true, "Expected function in: {}", response.body); - assert_security_headers(&response.headers); -} - -#[test] -fn should_serve_home() { - // given - let server = serve_ui(); - - // when - let response = request(server, - "\ - GET / HTTP/1.1\r\n\ - Host: 127.0.0.1:8080\r\n\ - Connection: close\r\n\ - \r\n\ - {} - " - ); - - // then - response.assert_status("HTTP/1.1 200 OK"); - response.assert_header("Content-Type", "text/html"); - assert_security_headers(&response.headers); -} - - -#[test] -fn should_inject_js() { - // given - let server = serve_ui(); - - // when - let response = request(server, - "\ - GET / HTTP/1.1\r\n\ - Host: 127.0.0.1:8080\r\n\ - Connection: close\r\n\ - \r\n\ - {} - " - ); - - // then - response.assert_status("HTTP/1.1 200 OK"); - response.assert_header("Content-Type", "text/html"); - assert_eq!( - response.body.contains(r#"/inject.js">"#), - true, - "Expected inject script tag in: {}", - response.body - ); - assert_security_headers(&response.headers); -} diff --git a/dapps/src/tests/mod.rs b/dapps/src/tests/mod.rs index a47294392ea8e1b5b9f2df39c2681a7f8b9f7132..c4d88cf9f172d6846fad759315c2d69b64497b2e 100644 --- a/dapps/src/tests/mod.rs +++ b/dapps/src/tests/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -20,8 +20,5 @@ mod helpers; mod api; mod fetch; -mod home; -mod redirection; mod rpc; mod validation; - diff --git a/dapps/src/tests/redirection.rs b/dapps/src/tests/redirection.rs deleted file mode 100644 index b7f72009f72f987394d53960aad62a6079e02f09..0000000000000000000000000000000000000000 --- a/dapps/src/tests/redirection.rs +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright 2015-2017 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 . - -use tests::helpers::{serve, request, assert_security_headers, assert_security_headers_for_embed}; - -#[test] -fn should_redirect_to_home() { - // given - let server = serve(); - - // when - let response = request(server, - "\ - GET / HTTP/1.1\r\n\ - Host: 127.0.0.1:8080\r\n\ - Connection: close\r\n\ - \r\n\ - " - ); - - // then - response.assert_status("HTTP/1.1 302 Found"); - assert_eq!(response.headers.get(0).unwrap(), "Location: http://127.0.0.1:18180"); -} - -#[test] -fn should_redirect_to_home_with_domain() { - // given - let server = serve(); - - // when - let response = request(server, - "\ - GET / HTTP/1.1\r\n\ - Host: home.web3.site\r\n\ - Connection: close\r\n\ - \r\n\ - " - ); - - // then - response.assert_status("HTTP/1.1 302 Found"); - assert_eq!(response.headers.get(0).unwrap(), "Location: http://127.0.0.1:18180"); -} - -#[test] -fn should_redirect_to_home_when_trailing_slash_is_missing() { - // given - let server = serve(); - - // when - let response = request(server, - "\ - GET /app HTTP/1.1\r\n\ - Host: 127.0.0.1:8080\r\n\ - Connection: close\r\n\ - \r\n\ - " - ); - - // then - response.assert_status("HTTP/1.1 302 Found"); - assert_eq!(response.headers.get(0).unwrap(), "Location: http://127.0.0.1:18180"); -} - -#[test] -fn should_display_404_on_invalid_dapp() { - // given - let server = serve(); - - // when - let response = request(server, - "\ - GET /invaliddapp/ HTTP/1.1\r\n\ - Host: 127.0.0.1:8080\r\n\ - Connection: close\r\n\ - \r\n\ - " - ); - - // then - response.assert_status("HTTP/1.1 404 Not Found"); - assert_security_headers_for_embed(&response.headers); -} - -#[test] -fn should_display_404_on_invalid_dapp_with_domain() { - // given - let server = serve(); - - // when - let response = request(server, - "\ - GET / HTTP/1.1\r\n\ - Host: invaliddapp.web3.site\r\n\ - Connection: close\r\n\ - \r\n\ - " - ); - - // then - response.assert_status("HTTP/1.1 404 Not Found"); - assert_security_headers_for_embed(&response.headers); -} - -#[test] -fn should_serve_rpc() { - // given - let server = serve(); - - // when - let response = request(server, - "\ - POST / HTTP/1.1\r\n\ - Host: 127.0.0.1:8080\r\n\ - Connection: close\r\n\ - Content-Type: application/json\r\n - \r\n\ - {} - " - ); - - // then - response.assert_status("HTTP/1.1 200 OK"); - assert_eq!(response.body, format!("4C\n{}\n\n0\n\n", r#"{"jsonrpc":"2.0","error":{"code":-32700,"message":"Parse error"},"id":null}"#)); -} - -#[test] -fn should_serve_rpc_at_slash_rpc() { - // given - let server = serve(); - - // when - let response = request(server, - "\ - POST /rpc HTTP/1.1\r\n\ - Host: 127.0.0.1:8080\r\n\ - Connection: close\r\n\ - Content-Type: application/json\r\n - \r\n\ - {} - " - ); - - // then - response.assert_status("HTTP/1.1 200 OK"); - assert_eq!(response.body, format!("4C\n{}\n\n0\n\n", r#"{"jsonrpc":"2.0","error":{"code":-32700,"message":"Parse error"},"id":null}"#)); -} - - -#[test] -fn should_serve_proxy_pac() { - // given - let server = serve(); - - // when - let response = request(server, - "\ - GET /proxy/proxy.pac HTTP/1.1\r\n\ - Host: 127.0.0.1:8080\r\n\ - Connection: close\r\n\ - \r\n\ - {} - " - ); - - // then - response.assert_status("HTTP/1.1 200 OK"); - assert_eq!(response.body, "DB\n\nfunction FindProxyForURL(url, host) {\n\tif (shExpMatch(host, \"home.web3.site\"))\n\t{\n\t\treturn \"PROXY 127.0.0.1:18180\";\n\t}\n\n\tif (shExpMatch(host, \"*.web3.site\"))\n\t{\n\t\treturn \"PROXY 127.0.0.1:8080\";\n\t}\n\n\treturn \"DIRECT\";\n}\n\n0\n\n".to_owned()); - assert_security_headers(&response.headers); -} - -#[test] -fn should_serve_utils() { - // given - let server = serve(); - - // when - let response = request(server, - "\ - GET /parity-utils/inject.js HTTP/1.1\r\n\ - Host: 127.0.0.1:8080\r\n\ - Connection: close\r\n\ - \r\n\ - {} - " - ); - - // then - response.assert_status("HTTP/1.1 200 OK"); - response.assert_header("Content-Type", "application/javascript"); - assert_eq!(response.body.contains("function(){"), true, "Expected function in: {}", response.body); - assert_security_headers(&response.headers); -} diff --git a/dapps/src/tests/rpc.rs b/dapps/src/tests/rpc.rs index 0cfc2c5a812b79edcb0ab858181f488ee8e273eb..326fcd72a636d27fe0651e8b7c681be90679357f 100644 --- a/dapps/src/tests/rpc.rs +++ b/dapps/src/tests/rpc.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/dapps/src/tests/validation.rs b/dapps/src/tests/validation.rs index bd97c940a04c67f38cc89f125741462d5e688f66..f9d22f802d8ededa26a5f15f5dd4e230f6ea9d37 100644 --- a/dapps/src/tests/validation.rs +++ b/dapps/src/tests/validation.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -37,26 +37,6 @@ fn should_reject_invalid_host() { assert!(response.body.contains("Provided Host header is not whitelisted."), response.body); } -#[test] -fn should_allow_valid_host() { - // given - let server = serve_hosts(Some(vec!["localhost:8080".into()])); - - // when - let response = request(server, - "\ - GET /ui/ HTTP/1.1\r\n\ - Host: localhost:8080\r\n\ - Connection: close\r\n\ - \r\n\ - {} - " - ); - - // then - response.assert_status("HTTP/1.1 200 OK"); -} - #[test] fn should_serve_dapps_domains() { // given @@ -66,28 +46,7 @@ fn should_serve_dapps_domains() { let response = request(server, "\ GET / HTTP/1.1\r\n\ - Host: ui.web3.site\r\n\ - Connection: close\r\n\ - \r\n\ - {} - " - ); - - // then - response.assert_status("HTTP/1.1 200 OK"); -} - -#[test] -// NOTE [todr] This is required for error pages to be styled properly. -fn should_allow_parity_utils_even_on_invalid_domain() { - // given - let server = serve_hosts(Some(vec!["localhost:8080".into()])); - - // when - let response = request(server, - "\ - GET /parity-utils/styles.css HTTP/1.1\r\n\ - Host: 127.0.0.1:8080\r\n\ + Host: proxy.web3.site\r\n\ Connection: close\r\n\ \r\n\ {} diff --git a/dapps/src/web.rs b/dapps/src/web.rs index 86c0ac28d6c8d01bae482f60e2df36bfa69a3942..fac0dca1afd9a9c107a18fc60e0daeb46a446218 100644 --- a/dapps/src/web.rs +++ b/dapps/src/web.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -30,10 +30,9 @@ use handlers::{ ContentFetcherHandler, ContentHandler, ContentValidator, ValidatorResponse, StreamingHandler, }; -use {Embeddable, WebProxyTokens}; +use WebProxyTokens; pub struct Web { - embeddable_on: Embeddable, web_proxy_tokens: Arc, fetch: F, pool: CpuPool, @@ -41,13 +40,11 @@ pub struct Web { impl Web { pub fn boxed( - embeddable_on: Embeddable, web_proxy_tokens: Arc, fetch: F, pool: CpuPool, ) -> Box { Box::new(Web { - embeddable_on, web_proxy_tokens, fetch, pool, @@ -64,7 +61,6 @@ impl Web { "Invalid parameter", "Couldn't parse given parameter:", path.app_params.get(0).map(String::as_str), - self.embeddable_on.clone() ))?; let mut token_it = token_and_url.split('+'); @@ -76,7 +72,7 @@ impl Web { Some(domain) => domain, _ => { return Err(ContentHandler::error( - StatusCode::BadRequest, "Invalid Access Token", "Invalid or old web proxy access token supplied.", Some("Try refreshing the page."), self.embeddable_on.clone() + StatusCode::BadRequest, "Invalid Access Token", "Invalid or old web proxy access token supplied.", Some("Try refreshing the page."), )); } }; @@ -86,14 +82,14 @@ impl Web { Some(url) if url.starts_with("http://") || url.starts_with("https://") => url.to_owned(), _ => { return Err(ContentHandler::error( - StatusCode::BadRequest, "Invalid Protocol", "Invalid protocol used.", None, self.embeddable_on.clone() + StatusCode::BadRequest, "Invalid Protocol", "Invalid protocol used.", None, )); } }; if !target_url.starts_with(&*domain) { return Err(ContentHandler::error( - StatusCode::BadRequest, "Invalid Domain", "Dapp attempted to access invalid domain.", Some(&target_url), self.embeddable_on.clone(), + StatusCode::BadRequest, "Invalid Domain", "Dapp attempted to access invalid domain.", Some(&target_url), )); } @@ -128,10 +124,8 @@ impl Endpoint for Web { &target_url, path, WebInstaller { - embeddable_on: self.embeddable_on.clone(), token, }, - self.embeddable_on.clone(), self.fetch.clone(), self.pool.clone(), )) @@ -139,7 +133,6 @@ impl Endpoint for Web { } struct WebInstaller { - embeddable_on: Embeddable, token: String, } @@ -154,12 +147,10 @@ impl ContentValidator for WebInstaller { fetch::BodyReader::new(response), status, mime, - self.embeddable_on, ); if is_html { handler.set_initial_content(&format!( - r#""#, - apps::UTILS_PATH, + r#""#, apps::URL_REFERER, apps::WEB_PATH, &self.token, @@ -168,4 +159,3 @@ impl ContentValidator for WebInstaller { Ok(ValidatorResponse::Streaming(handler)) } } - diff --git a/dapps/ui/Cargo.toml b/dapps/ui/Cargo.toml deleted file mode 100644 index e7f409b1c1cdadefd7abc1e1d2984a4c9c44df23..0000000000000000000000000000000000000000 --- a/dapps/ui/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -description = "Ethcore Parity UI" -homepage = "http://parity.io" -license = "GPL-3.0" -name = "parity-ui" -version = "1.11.0" -authors = ["Parity Technologies "] - -[build-dependencies] -rustc_version = "0.2" - -[dependencies] -parity-ui-dev = { git = "https://github.com/parity-js/shell.git", rev = "eecaadcb9e421bce31e91680d14a20bbd38f92a2", optional = true } -parity-ui-old-dev = { git = "https://github.com/parity-js/dapp-wallet.git", rev = "65deb02e7c007a0fd8aab0c089c93e3fd1de6f87", optional = true } -parity-ui-precompiled = { git = "https://github.com/js-dist-paritytech/parity-master-1-10-shell.git", rev="bd25b41cd642c6b822d820dded3aa601a29aa079", optional = true } -parity-ui-old-precompiled = { git = "https://github.com/js-dist-paritytech/parity-master-1-10-wallet.git", rev="4b6f112412716cd05123d32eeb7fda448288a6c6", optional = true } - -[features] -no-precompiled-js = ["parity-ui-dev", "parity-ui-old-dev"] -use-precompiled-js = ["parity-ui-precompiled", "parity-ui-old-precompiled"] diff --git a/devtools/Cargo.toml b/devtools/Cargo.toml index d5e275a3adbcc8e857006883f3c68244ff48945f..71eeaa2ddd0935ce08c691bdc557cae143c21023 100644 --- a/devtools/Cargo.toml +++ b/devtools/Cargo.toml @@ -3,5 +3,5 @@ description = "Ethcore development/test/build tools" homepage = "http://parity.io" license = "GPL-3.0" name = "ethcore-devtools" -version = "1.11.0" +version = "1.12.0" authors = ["Parity Technologies "] diff --git a/devtools/src/http_client.rs b/devtools/src/http_client.rs index ab234105929077c3d777e2b5ac37a733a2bfbe2c..e2f33d4257fbc071c466a171249f1ac4021ba391 100644 --- a/devtools/src/http_client.rs +++ b/devtools/src/http_client.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/devtools/src/lib.rs b/devtools/src/lib.rs index efaf4b9351acb9e04e65e4eabcb68f9310925789..6fdbc8d8902edd4d7c224ef1b494b51b10aff67c 100644 --- a/devtools/src/lib.rs +++ b/devtools/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/docker/alpine/Dockerfile b/docker/alpine/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..4d08afd3f8f68172d3f758b04aa25789c6492273 --- /dev/null +++ b/docker/alpine/Dockerfile @@ -0,0 +1,29 @@ +FROM alpine:edge + +WORKDIR /build + +# install tools and dependencies +RUN apk add --no-cache gcc musl-dev pkgconfig g++ make curl \ + eudev-dev rust cargo git file binutils \ + libusb-dev linux-headers perl + +# show backtraces +ENV RUST_BACKTRACE 1 + +# show tools +RUN rustc -vV && \ +cargo -V && \ +gcc -v &&\ +g++ -v + +# build parity +ADD . /build/parity +RUN cd parity && \ + cargo build --release --verbose && \ + ls /build/parity/target/release/parity && \ + strip /build/parity/target/release/parity + +RUN file /build/parity/target/release/parity + +EXPOSE 8080 8545 8180 +ENTRYPOINT ["/build/parity/target/release/parity"] diff --git a/docker/android/Dockerfile b/docker/android/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..7783781b4cad35a2493bb7352dad6ff8920fd842 --- /dev/null +++ b/docker/android/Dockerfile @@ -0,0 +1,79 @@ +FROM ubuntu:xenial +LABEL maintainer="Parity Technologies " + +RUN apt-get update && \ + apt-get install -yq sudo curl file build-essential wget git g++ cmake pkg-config bison flex \ + unzip lib32stdc++6 lib32z1 python autotools-dev automake autoconf libtool \ + gperf xsltproc docbook-xsl + +# Rust & Cargo +RUN curl https://sh.rustup.rs -sSf | sh -s -- -y +ENV PATH /root/.cargo/bin:$PATH +RUN rustup toolchain install stable +RUN rustup target add --toolchain stable arm-linux-androideabi +RUN rustup target add --toolchain stable armv7-linux-androideabi + +# Android NDK and toolchain +RUN cd /usr/local && \ + wget -q https://dl.google.com/android/repository/android-ndk-r16b-linux-x86_64.zip && \ + unzip -q android-ndk-r16b-linux-x86_64.zip && \ + rm android-ndk-r16b-linux-x86_64.zip +ENV NDK_HOME /usr/local/android-ndk-r16b +RUN /usr/local/android-ndk-r16b/build/tools/make-standalone-toolchain.sh \ + --arch=arm --install-dir=/opt/ndk-standalone --stl=libc++ --platform=android-26 +ENV PATH $PATH:/opt/ndk-standalone/bin + +# Compiling OpenSSL for Android +RUN cd /root && \ + git clone git://git.openssl.org/openssl.git && \ + cd openssl && \ + git checkout OpenSSL_1_1_0-stable +ENV CROSS_SYSROOT /opt/ndk-standalone/sysroot +RUN cd /root/openssl && \ + ./Configure android-armeabi --cross-compile-prefix=arm-linux-androideabi- \ + -static no-stdio no-ui \ + -I/usr/local/android-ndk-r16b/sysroot/usr/include \ + -I/usr/local/android-ndk-r16b/sysroot/usr/include/arm-linux-androideabi \ + -L/usr/local/android-ndk-r16b/sysroot/usr/lib \ + --prefix=/opt/ndk-standalone/sysroot/usr +RUN cd /root/openssl && \ + make build_libs && \ + make install_dev +RUN rm -rf /root/openssl + +# Compiling libudev for Android +# This is the most hacky part of the process, as we need to apply a patch and pass specific +# options that the compiler environment doesn't define. +RUN cd /root && \ + git clone https://github.com/gentoo/eudev.git +ADD libudev.patch /root +RUN cd /root/eudev && \ + git checkout 83d918449f22720d84a341a05e24b6d109e6d3ae && \ + ./autogen.sh && \ + ./configure --disable-introspection --disable-programs --disable-hwdb \ + --host=arm-linux-androideabi --prefix=/opt/ndk-standalone/sysroot/usr/ \ + --enable-shared=false CC=arm-linux-androideabi-clang \ + CFLAGS="-D LINE_MAX=2048 -D RLIMIT_NLIMITS=15 -D IPTOS_LOWCOST=2 -std=gnu99" \ + CXX=arm-linux-androideabi-clang++ && \ + git apply - < /root/libudev.patch && \ + make && \ + make install +RUN rm -rf /root/eudev +RUN rm /root/libudev.patch + +# Rust-related configuration +ADD cargo-config.toml /root/.cargo/config +ENV ARM_LINUX_ANDROIDEABI_OPENSSL_DIR /opt/ndk-standalone/sysroot/usr +ENV ARMV7_LINUX_ANDROIDEABI_OPENSSL_DIR /opt/ndk-standalone/sysroot/usr +ENV CC_arm_linux_androideabi arm-linux-androideabi-clang +ENV CC_armv7_linux_androideabi arm-linux-androideabi-clang +ENV CXX_arm_linux_androideabi arm-linux-androideabi-clang++ +ENV CXX_armv7_linux_androideabi arm-linux-androideabi-clang++ +ENV AR_arm_linux_androideabi arm-linux-androideabi-ar +ENV AR_armv7_linux_androideabi arm-linux-androideabi-ar +ENV CFLAGS_arm_linux_androideabi -std=gnu11 -fPIC -D OS_ANDROID +ENV CFLAGS_armv7_linux_androideabi -std=gnu11 -fPIC -D OS_ANDROID +ENV CXXFLAGS_arm_linux_androideabi -std=gnu++11 -fPIC -fexceptions -frtti -static-libstdc++ -D OS_ANDROID +ENV CXXFLAGS_armv7_linux_androideabi -std=gnu++11 -fPIC -fexceptions -frtti -static-libstdc++ -D OS_ANDROID +ENV CXXSTDLIB_arm_linux_androideabi "" +ENV CXXSTDLIB_armv7_linux_androideabi "" diff --git a/docker/android/cargo-config.toml b/docker/android/cargo-config.toml new file mode 100644 index 0000000000000000000000000000000000000000..7373a7e143d9a500b432b93f5355c7ccba2f8bab --- /dev/null +++ b/docker/android/cargo-config.toml @@ -0,0 +1,9 @@ +[target.armv7-linux-androideabi] +linker = "arm-linux-androideabi-clang" +ar = "arm-linux-androideabi-ar" +rustflags = ["-C", "link-arg=-lc++_static", "-C", "link-arg=-lc++abi", "-C", "link-arg=-landroid_support"] + +[target.arm-linux-androideabi] +linker = "arm-linux-androideabi-clang" +ar = "arm-linux-androideabi-ar" +rustflags = ["-C", "link-arg=-lc++_static", "-C", "link-arg=-lc++abi", "-C", "link-arg=-landroid_support"] diff --git a/docker/android/libudev.patch b/docker/android/libudev.patch new file mode 100644 index 0000000000000000000000000000000000000000..ba7e849b2e6397c6d23f131809eefbbaea4d49ec --- /dev/null +++ b/docker/android/libudev.patch @@ -0,0 +1,216 @@ +diff --git a/src/collect/collect.c b/src/collect/collect.c +index 2cf1f00..b24f26b 100644 +--- a/src/collect/collect.c ++++ b/src/collect/collect.c +@@ -84,7 +84,7 @@ static void usage(void) + " invoked for each ID in ) collect returns 0, the\n" + " number of missing IDs otherwise.\n" + " On error a negative number is returned.\n\n" +- , program_invocation_short_name); ++ , "parity"); + } + + /* +diff --git a/src/scsi_id/scsi_id.c b/src/scsi_id/scsi_id.c +index 8b76d87..7bf3948 100644 +--- a/src/scsi_id/scsi_id.c ++++ b/src/scsi_id/scsi_id.c +@@ -321,7 +321,7 @@ static void help(void) { + " -u --replace-whitespace Replace all whitespace by underscores\n" + " -v --verbose Verbose logging\n" + " -x --export Print values as environment keys\n" +- , program_invocation_short_name); ++ , "parity"); + + } + +diff --git a/src/shared/hashmap.h b/src/shared/hashmap.h +index a03ee58..a7c2005 100644 +--- a/src/shared/hashmap.h ++++ b/src/shared/hashmap.h +@@ -98,10 +98,7 @@ extern const struct hash_ops uint64_hash_ops; + #if SIZEOF_DEV_T != 8 + unsigned long devt_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) _pure_; + int devt_compare_func(const void *a, const void *b) _pure_; +-extern const struct hash_ops devt_hash_ops = { +- .hash = devt_hash_func, +- .compare = devt_compare_func +-}; ++extern const struct hash_ops devt_hash_ops; + #else + #define devt_hash_func uint64_hash_func + #define devt_compare_func uint64_compare_func +diff --git a/src/shared/log.c b/src/shared/log.c +index 4a40996..1496984 100644 +--- a/src/shared/log.c ++++ b/src/shared/log.c +@@ -335,7 +335,7 @@ static int write_to_syslog( + + IOVEC_SET_STRING(iovec[0], header_priority); + IOVEC_SET_STRING(iovec[1], header_time); +- IOVEC_SET_STRING(iovec[2], program_invocation_short_name); ++ IOVEC_SET_STRING(iovec[2], "parity"); + IOVEC_SET_STRING(iovec[3], header_pid); + IOVEC_SET_STRING(iovec[4], buffer); + +@@ -383,7 +383,7 @@ static int write_to_kmsg( + char_array_0(header_pid); + + IOVEC_SET_STRING(iovec[0], header_priority); +- IOVEC_SET_STRING(iovec[1], program_invocation_short_name); ++ IOVEC_SET_STRING(iovec[1], "parity"); + IOVEC_SET_STRING(iovec[2], header_pid); + IOVEC_SET_STRING(iovec[3], buffer); + IOVEC_SET_STRING(iovec[4], "\n"); +diff --git a/src/udev/udevadm-control.c b/src/udev/udevadm-control.c +index 6af7163..3271e56 100644 +--- a/src/udev/udevadm-control.c ++++ b/src/udev/udevadm-control.c +@@ -41,7 +41,7 @@ static void print_help(void) { + " -p --property=KEY=VALUE Set a global property for all events\n" + " -m --children-max=N Maximum number of children\n" + " --timeout=SECONDS Maximum time to block for a reply\n" +- , program_invocation_short_name); ++ , "parity"); + } + + static int adm_control(struct udev *udev, int argc, char *argv[]) { +diff --git a/src/udev/udevadm-info.c b/src/udev/udevadm-info.c +index 0aec976..a31ac02 100644 +--- a/src/udev/udevadm-info.c ++++ b/src/udev/udevadm-info.c +@@ -279,7 +279,7 @@ static void help(void) { + " -P --export-prefix Export the key name with a prefix\n" + " -e --export-db Export the content of the udev database\n" + " -c --cleanup-db Clean up the udev database\n" +- , program_invocation_short_name); ++ , "parity"); + } + + static int uinfo(struct udev *udev, int argc, char *argv[]) { +diff --git a/src/udev/udevadm-monitor.c b/src/udev/udevadm-monitor.c +index 15ded09..b58dd08 100644 +--- a/src/udev/udevadm-monitor.c ++++ b/src/udev/udevadm-monitor.c +@@ -73,7 +73,7 @@ static void help(void) { + " -u --udev Print udev events\n" + " -s --subsystem-match=SUBSYSTEM[/DEVTYPE] Filter events by subsystem\n" + " -t --tag-match=TAG Filter events by tag\n" +- , program_invocation_short_name); ++ , "parity"); + } + + static int adm_monitor(struct udev *udev, int argc, char *argv[]) { +diff --git a/src/udev/udevadm-settle.c b/src/udev/udevadm-settle.c +index 33597bc..b36a504 100644 +--- a/src/udev/udevadm-settle.c ++++ b/src/udev/udevadm-settle.c +@@ -43,7 +43,7 @@ static void help(void) { + " --version Show package version\n" + " -t --timeout=SECONDS Maximum time to wait for events\n" + " -E --exit-if-exists=FILE Stop waiting if file exists\n" +- , program_invocation_short_name); ++ , "parity"); + } + + static int adm_settle(struct udev *udev, int argc, char *argv[]) { +diff --git a/src/udev/udevadm-test-builtin.c b/src/udev/udevadm-test-builtin.c +index baaeca9..50ed812 100644 +--- a/src/udev/udevadm-test-builtin.c ++++ b/src/udev/udevadm-test-builtin.c +@@ -39,7 +39,7 @@ static void help(struct udev *udev) { + " -h --help Print this message\n" + " --version Print version of the program\n\n" + "Commands:\n" +- , program_invocation_short_name); ++ , "parity"); + + udev_builtin_list(udev); + } +diff --git a/src/udev/udevadm-test.c b/src/udev/udevadm-test.c +index 47fd924..a855412 100644 +--- a/src/udev/udevadm-test.c ++++ b/src/udev/udevadm-test.c +@@ -39,7 +39,7 @@ static void help(void) { + " --version Show package version\n" + " -a --action=ACTION Set action string\n" + " -N --resolve-names=early|late|never When to resolve names\n" +- , program_invocation_short_name); ++ , "parity"); + } + + static int adm_test(struct udev *udev, int argc, char *argv[]) { +diff --git a/src/udev/udevadm-trigger.c b/src/udev/udevadm-trigger.c +index 4dc756a..67787d3 100644 +--- a/src/udev/udevadm-trigger.c ++++ b/src/udev/udevadm-trigger.c +@@ -92,7 +92,7 @@ static void help(void) { + " -y --sysname-match=NAME Trigger devices with this /sys path\n" + " --name-match=NAME Trigger devices with this /dev name\n" + " -b --parent-match=NAME Trigger devices with that parent device\n" +- , program_invocation_short_name); ++ , "parity"); + } + + static int adm_trigger(struct udev *udev, int argc, char *argv[]) { +diff --git a/src/udev/udevadm.c b/src/udev/udevadm.c +index 3e57cf6..b03dfaa 100644 +--- a/src/udev/udevadm.c ++++ b/src/udev/udevadm.c +@@ -62,7 +62,7 @@ static int adm_help(struct udev *udev, int argc, char *argv[]) { + printf("%s [--help] [--version] [--debug] COMMAND [COMMAND OPTIONS]\n\n" + "Send control commands or test the device manager.\n\n" + "Commands:\n" +- , program_invocation_short_name); ++ , "parity"); + + for (i = 0; i < ELEMENTSOF(udevadm_cmds); i++) + if (udevadm_cmds[i]->help != NULL) +@@ -128,7 +128,7 @@ int main(int argc, char *argv[]) { + goto out; + } + +- fprintf(stderr, "%s: missing or unknown command\n", program_invocation_short_name); ++ fprintf(stderr, "%s: missing or unknown command\n", "parity"); + rc = 2; + out: + mac_selinux_finish(); +diff --git a/src/udev/udevd.c b/src/udev/udevd.c +index cf826c6..4eec0af 100644 +--- a/src/udev/udevd.c ++++ b/src/udev/udevd.c +@@ -1041,7 +1041,7 @@ static void help(void) { + " -t --event-timeout=SECONDS Seconds to wait before terminating an event\n" + " -N --resolve-names=early|late|never\n" + " When to resolve users and groups\n" +- , program_invocation_short_name); ++ , "parity"); + } + + static int parse_argv(int argc, char *argv[]) { +diff --git a/src/v4l_id/v4l_id.c b/src/v4l_id/v4l_id.c +index 1dce0d5..f65badf 100644 +--- a/src/v4l_id/v4l_id.c ++++ b/src/v4l_id/v4l_id.c +@@ -49,7 +49,7 @@ int main(int argc, char *argv[]) { + printf("%s [-h,--help] \n\n" + "Video4Linux device identification.\n\n" + " -h Print this message\n" +- , program_invocation_short_name); ++ , "parity"); + return 0; + case '?': + return -EINVAL; +diff --git a/src/shared/path-util.c b/src/shared/path-util.c +index 0744563..7151356 100644 +--- a/src/shared/path-util.c ++++ b/src/shared/path-util.c +@@ -109,7 +109,7 @@ char *path_make_absolute_cwd(const char *p) { + if (path_is_absolute(p)) + return strdup(p); + +- cwd = get_current_dir_name(); ++ cwd = getcwd(malloc(128), 128); + if (!cwd) + return NULL; + diff --git a/docker/hub/Dockerfile b/docker/hub/Dockerfile index 661542472d12b09cfeabadfe4b570790fbdf90f9..b5f8178290a7385c4908759d9a82f6d67e9677b8 100644 --- a/docker/hub/Dockerfile +++ b/docker/hub/Dockerfile @@ -27,20 +27,7 @@ RUN apt-get update && \ libudev-dev \ pkg-config \ dpkg-dev \ - # evmjit dependencies - zlib1g-dev \ - libedit-dev \ - libudev-dev &&\ -# cmake and llvm ppa's. then update ppa's - add-apt-repository -y "ppa:george-edison55/cmake-3.x" && \ - add-apt-repository "deb http://llvm.org/apt/trusty/ llvm-toolchain-trusty-3.7 main" && \ - apt-get update && \ - apt-get install -y --force-yes cmake llvm-3.7-dev && \ -# install evmjit - git clone https://github.com/debris/evmjit && \ - cd evmjit && \ - mkdir build && cd build && \ - cmake .. && make && make install && cd && \ + libudev-dev &&\ # install rustup curl https://sh.rustup.rs -sSf | sh -s -- -y && \ # rustup directory @@ -71,11 +58,7 @@ cd /build&&git clone https://github.com/slockit/parity -b in3 && \ binutils \ file \ pkg-config \ - dpkg-dev \ - # evmjit dependencies - zlib1g-dev \ - libedit-dev \ - cmake llvm-3.7-dev&&\ + dpkg-dev &&\ rm -rf /var/lib/apt/lists/* # setup ENTRYPOINT EXPOSE 8080 8545 8180 diff --git a/docker/ubuntu-jit/Dockerfile b/docker/ubuntu-jit/Dockerfile deleted file mode 100644 index 610d07c7dcbdf5c66e2e86a8a50fe74bfe70b84c..0000000000000000000000000000000000000000 --- a/docker/ubuntu-jit/Dockerfile +++ /dev/null @@ -1,58 +0,0 @@ -FROM ubuntu:14.04 -WORKDIR /build - -# install tools and dependencies -RUN apt-get update && \ - apt-get install -y \ - # make - build-essential \ - # add-apt-repository - software-properties-common \ - curl \ - wget \ - git \ - g++ \ - binutils \ - file \ - # evmjit dependencies - zlib1g-dev \ - libedit-dev - -# cmake and llvm ppas. then update ppas -RUN add-apt-repository -y "ppa:george-edison55/cmake-3.x" && \ - add-apt-repository "deb http://llvm.org/apt/trusty/ llvm-toolchain-trusty-3.7 main" && \ - apt-get update && \ - apt-get install -y --force-yes cmake llvm-3.7-dev - -# install evmjit -RUN git clone https://github.com/debris/evmjit && \ - cd evmjit && \ - mkdir build && cd build && \ - cmake .. && make && make install && cd - -# install rustup -RUN curl https://sh.rustup.rs -sSf | sh -s -- -y - -# rustup directory -ENV PATH /root/.cargo/bin:$PATH - -# show backtraces -ENV RUST_BACKTRACE 1 - -# show tools -RUN rustc -vV && \ -cargo -V && \ -gcc -v &&\ -g++ -v - -# build parity -ADD . /build/parity -RUN cd parity && \ - cargo build --release --features ethcore/jit --verbose && \ - ls /build/parity/target/release/parity && \ - strip /build/parity/target/release/parity - -RUN file /build/parity/target/release/parity - -EXPOSE 8080 8545 8180 -ENTRYPOINT ["/build/parity/target/release/parity"] diff --git a/docs/CHANGELOG-1.10.md b/docs/CHANGELOG-1.10.md new file mode 100644 index 0000000000000000000000000000000000000000..1c15e456ea4512ef20b531e9b13835dab9b46d11 --- /dev/null +++ b/docs/CHANGELOG-1.10.md @@ -0,0 +1,386 @@ +## Parity [v1.10.6](https://github.com/paritytech/parity/releases/tag/v1.10.6) (2018-06-05) + +Parity 1.10.6 is a security-relevant release. Please upgrade your nodes as soon as possible. + +If you can not upgrade to 1.10+ yet, please use the following branches and build your own binaries from source: + +- git checkout [old-stable-1.9](https://github.com/paritytech/parity/tree/old-stable-1.9) # `v1.9.8` (EOL) +- git checkout [old-stable-1.8](https://github.com/paritytech/parity/tree/old-stable-1.8) # `v1.8.12` (EOL) +- git checkout [old-stable-1.7](https://github.com/paritytech/parity/tree/old-stable-1.7) # `v1.7.14` (EOL) + +The full list of included changes: + +- Parity-version: bump stable to 1.10.6 ([#8805](https://github.com/paritytech/parity/pull/8805)) + - Parity-version: bump stable to 1.10.6 + - Disallow unsigned transactions in case EIP-86 is disabled ([#8802](https://github.com/paritytech/parity/pull/8802)) +- Update shell32-sys to fix windows build ([#8793](https://github.com/paritytech/parity/pull/8793)) +- Backports ([#8782](https://github.com/paritytech/parity/pull/8782)) + - Fix light sync with initial validator-set contract ([#8528](https://github.com/paritytech/parity/pull/8528)) + - Fix #8468 + - Use U256::max_value() instead + - Fix again + - Also change initial transaction gas + - Don't open Browser post-install on Mac ([#8641](https://github.com/paritytech/parity/pull/8641)) + - Prefix uint fmt with `0x` with alternate flag + - Set the request index to that of the current request ([#8683](https://github.com/paritytech/parity/pull/8683)) + - Set the request index to that of the current request + - Node table sorting according to last contact data ([#8541](https://github.com/paritytech/parity/pull/8541)) + - Network-devp2p: sort nodes in node table using last contact data + - Network-devp2p: rename node contact types in node table json output + - Network-devp2p: fix node table tests + - Network-devp2p: note node failure when failed to establish connection + - Network-devp2p: handle UselessPeer error + - Network-devp2p: note failure when marking node as useless + - Network-devp2p: handle UselessPeer disconnect ([#8686](https://github.com/paritytech/parity/pull/8686)) +- Parity: bump stable version to 1.10.5 ([#8749](https://github.com/paritytech/parity/pull/8749)) + - Parity: bump stable version to 1.10.5 + - Fix failing doc tests running on non-code + +## Parity [v1.10.4](https://github.com/paritytech/parity/releases/tag/v1.10.4) (2018-05-15) + +Parity 1.10.4 is a bug-fix release to improve performance and stability. + +The full list of included changes: + +- Backports ([#8623](https://github.com/paritytech/parity/pull/8623)) + - Fix account list double 0x display ([#8596](https://github.com/paritytech/parity/pull/8596)) + - Remove unused self import + - Fix account list double 0x display + - Trace precompiled contracts when the transfer value is not zero ([#8486](https://github.com/paritytech/parity/pull/8486)) + - Trace precompiled contracts when the transfer value is not zero + - Add tests for precompiled CALL tracing + - Use byzantium test machine for the new test + - Add notes in comments on why we don't trace all precompileds + - Use is_transferred instead of transferred + - Gitlab test script fixes ([#8573](https://github.com/paritytech/parity/pull/8573)) + - Exclude /docs from modified files. + - Ensure all references in the working tree are available + - Remove duplicated line from test script +- Bump stable to 1.10.4 ([#8626](https://github.com/paritytech/parity/pull/8626)) +- Allow stable snaps to be stable. ([#8582](https://github.com/paritytech/parity/pull/8582)) + +## Parity [v1.10.3](https://github.com/paritytech/parity/releases/tag/v1.10.3) (2018-05-08) + +Parity 1.10.3 marks the first stable release on the 1.10 track. Among others, it improves performance and stability. + +The full list of included changes: + +- Backports ([#8557](https://github.com/paritytech/parity/pull/8557)) + - Update wasmi and pwasm-utils ([#8493](https://github.com/paritytech/parity/pull/8493)) + - Update wasmi to 0.2 + - Update pwasm-utils to 0.1.5 + - Fetching logs by hash in blockchain database ([#8463](https://github.com/paritytech/parity/pull/8463)) + - Fetch logs by hash in blockchain database + - Fix tests + - Add unit test for branch block logs fetching + - Add docs that blocks must already be sorted + - Handle branch block cases properly + - typo: empty -> is_empty + - Remove return_empty_if_none by using a closure + - Use BTreeSet to avoid sorting again + - Move is_canon to BlockChain + - typo: pass value by reference + - Use loop and wrap inside blocks to simplify the code + - typo: missed a comment + - Pass on storage keys tracing to handle the case when it is not modified ([#8491](https://github.com/paritytech/parity/pull/8491)) + - Pass on storage keys even if it is not modified + - typo: account and storage query `to_pod_diff` builds both `touched_addresses` merge and storage keys merge. + - Fix tests + - Use state query directly because of suicided accounts + - Fix a RefCell borrow issue + - Add tests for unmodified storage trace + - Address grumbles + - typo: remove unwanted empty line + - ensure_cached compiles with the original signature + - Enable WebAssembly and Byzantium for Ellaism ([#8520](https://github.com/paritytech/parity/pull/8520)) + - Enable WebAssembly and Byzantium for Ellaism + - Fix indentation + - Remove empty lines + - Fix compilation. +- Stabilize 1.10.3 ([#8474](https://github.com/paritytech/parity/pull/8474)) + - Stabelize 1.10 + - Bump stable to 1.10.3 + - Update Gitlab scripts + - Fix snap builds ([#8483](https://github.com/paritytech/parity/pull/8483)) + - Fix docker build ([#8462](https://github.com/paritytech/parity/pull/8462)) + - Use `master` as Docker's `latest` (`beta-release` is not used anymore) + +## Parity [v1.10.2](https://github.com/paritytech/parity/releases/tag/v1.10.2) (2018-04-24) + +Parity 1.10.2 is a bug-fix release to improve performance and stability. + +The full list of included changes: + +- Update Parity beta to 1.10.2 + Backports ([#8455](https://github.com/paritytech/parity/pull/8455)) + - Update Parity beta to 1.10.2 + - Allow 32-bit pipelines to fail ([#8454](https://github.com/paritytech/parity/pull/8454)) + - Disable 32-bit targets for Gitlab + - Rename Linux pipelines + - Update wasmi ([#8452](https://github.com/paritytech/parity/pull/8452)) + - Fix Cargo.lock +- Backports ([#8450](https://github.com/paritytech/parity/pull/8450)) + - Use forked app_dirs crate for reverted Windows dir behavior ([#8438](https://github.com/paritytech/parity/pull/8438)) + - Remove unused app_dirs dependency in CLI + - Use forked app_dirs crate for reverted Windows dir behavior + - Remove Tendermint extra_info due to seal inconsistencies ([#8367](https://github.com/paritytech/parity/pull/8367)) + - Handle queue import errors a bit more gracefully ([#8385](https://github.com/paritytech/parity/pull/8385)) + - Improve VM executor stack size estimation rules ([#8439](https://github.com/paritytech/parity/pull/8439)) + - Improve VM executor stack size estimation rules + - Typo: docs add "(Debug build)" comment + - Fix an off by one typo and set minimal stack size + - Use saturating_sub to avoid potential overflow + +## Parity [v1.10.1](https://github.com/paritytech/parity/releases/tag/v1.10.1) (2018-04-17) + +Parity 1.10.1 is a bug-fix release to improve performance and stability. Among other changes, you can now use `--warp-barrier [BLOCK]` to specify a minimum block number to `--warp` to. This is useful in cases where clients restore to outdated snapshots far behind the latest chain head. + +The full list of included changes: + +- Bump beta to 1.10.1 ([#8350](https://github.com/paritytech/parity/pull/8350)) + - Bump beta to 1.10.1 + - Unflag critical release +- Backports ([#8346](https://github.com/paritytech/parity/pull/8346)) + - Warp-only sync with warp-barrier [blocknumber] flag. ([#8228](https://github.com/paritytech/parity/pull/8228)) + - Warp-only sync with warp-after [blocknumber] flag. + - Fix tests. + - Fix configuration tests. + - Rename to warp barrier. + - Allow unsafe js eval on Parity Wallet. ([#8204](https://github.com/paritytech/parity/pull/8204)) + - Update musicoin spec in line with gmc v2.6.2 ([#8242](https://github.com/paritytech/parity/pull/8242)) + - Supress TemporaryInvalid verification failures. ([#8256](https://github.com/paritytech/parity/pull/8256)) + - Include suicided accounts in state diff ([#8297](https://github.com/paritytech/parity/pull/8297)) + - Include suicided accounts in state diff + - Shorten form match -> if let + - Test suicide trace diff in State + - Replace_home for password_files, reserved_peers and log_file ([#8324](https://github.com/paritytech/parity/pull/8324)) + - Replace_home for password_files, reserved_peers and log_file + - Typo: arg_log_file is Option + - Enable UI by default, but only display info page. + - Fix test. + - Fix naming and remove old todo. + - Change "wallet" with "browser UI" +- Change name Wallet -> UI ([#8164](https://github.com/paritytech/parity/pull/8164)) ([#8205](https://github.com/paritytech/parity/pull/8205)) + - Change name Wallet -> UI + - Make warning bold +- Backport [#8099](https://github.com/paritytech/parity/pull/8099) ([#8132](https://github.com/paritytech/parity/pull/8132)) +- WASM libs ([#8220](https://github.com/paritytech/parity/pull/8220)) + - Bump wasm libs ([#8171](https://github.com/paritytech/parity/pull/8171)) + - Bump wasmi version ([#8209](https://github.com/paritytech/parity/pull/8209)) +- Update hyper to 0.11.24 ([#8203](https://github.com/paritytech/parity/pull/8203)) +- Updated jsonrpc to include latest backports (beta) ([#8181](https://github.com/paritytech/parity/pull/8181)) + - Updated jsonrpc to include latest backports + - Update dependencies. + +## Parity [v1.10.0](https://github.com/paritytech/parity/releases/tag/v1.10.0) (2018-03-22) + +This is the Parity 1.10.0-beta release! Cool! + +### Disabling the Parity Wallet + +The **Parity Wallet (a.k.a. "UI") is now disabled by default**. We are preparing to split the wallet from the core client. + +To reactivate the parity wallet, you have to run Parity either with `parity --force-ui` (not recommended) or `parity ui` (deprecated) from the command line. Or, if you feel super fancy and want to test our pre-releases of the stand-alone electron wallet, head over to the [Parity-JS repositories and check the releases](https://github.com/Parity-JS/shell/releases). + +Further reading: + +- [Docs: Parity Wallet](https://wiki.parity.io/Parity-Wallet) +- [Docs: How to customize Parity UI?](https://wiki.parity.io/FAQ-Customize-Parity-UI.html) +- [Github: Parity-JS](https://github.com/parity-js) + +### Introducing the Wasm VM + +We are excited to announce support for **Wasm Smart Contracts on Kovan network**. The hard-fork to activate the Wasm-VM will take place on block `6_600_000`. + +To enable Wasm contracts on your custom network, just schedule a `wasmActivationTransition` at your favorite block number (e.g., `42`, `666`, or `0xbada55`). To hack your first Wasm smart contracts in Rust, have a look at the [Parity Wasm Tutorials](https://github.com/paritytech/pwasm-tutorial). + +Further reading: + +- [Docs: WebAssembly (wasm)](https://wiki.parity.io/WebAssembly-Home) +- [Docs: Wasm VM Design](https://wiki.parity.io/WebAssembly-Design) +- [Docs: Wasm tutorials and examples](https://wiki.parity.io/WebAssembly-Links) + +### Empty step messages in PoA + +To **reduce blockchain bloat, proof-of-authority networks can now enable _empty step messages_ which replace empty blocks**. Each step message will be signed and broadcasted by the issuing authorities, and included and rewarded in the next non-empty block. + +To enable empty step messages, set the `emptyStepsTransition` to your favorite block number. You can also specify a maximum number of empty steps with `maximumEmptySteps` in your chain spec. + +### Other noteworthy changes + +We removed the old database migrations from 2016. In case you upgrade Parity from a really, really old version, you will have to reset your database manually first with `parity db kill`. + +We fixed DELEGATECALL `from` and `to` fields, see [#7166](https://github.com/paritytech/parity/issues/7166). + +We reduced the default USD per transaction value to 0.0001. Thanks, @MysticRyuujin! + +The Musicoin chain is now enabled with Byzantium features starting at block `2_222_222`. + +### Overview of all changes included + +The full list of included changes: + +- Re-enable signer, even with no UI. ([#8167](https://github.com/paritytech/parity/pull/8167)) ([#8168](https://github.com/paritytech/parity/pull/8168)) + - Re-enable signer, even with no UI. + - Fix message. +- Beta Backports ([#8136](https://github.com/paritytech/parity/pull/8136)) + - Support parity protocol. ([#8035](https://github.com/paritytech/parity/pull/8035)) + - updater: apply exponential backoff after download failure ([#8059](https://github.com/paritytech/parity/pull/8059)) + - updater: apply exponential backoff after download failure + - updater: reset backoff on new release + - Max code size on Kovan ([#8067](https://github.com/paritytech/parity/pull/8067)) + - Enable code size limit on kovan + - Fix formatting. + - Limit incoming connections. ([#8060](https://github.com/paritytech/parity/pull/8060)) + - Limit ingress connections + - Optimized handshakes logging + - WASM libraries bump ([#7970](https://github.com/paritytech/parity/pull/7970)) + - update wasmi, parity-wasm, wasm-utils to latest version + - Update to new wasmi & error handling + - also utilize new stack limiter + - fix typo + - replace dependency url + - Cargo.lock update + - add some dos protection ([#8084](https://github.com/paritytech/parity/pull/8084)) + - revert removing blooms ([#8066](https://github.com/paritytech/parity/pull/8066)) + - Revert "fix traces, removed bloomchain crate, closes [#7228](https://github.com/paritytech/parity/issues/7228), closes [#7167](https://github.com/paritytech/parity/issues/7167)" + - Revert "fixed broken logs ([#7934](https://github.com/paritytech/parity/pull/7934))" + - fixed broken logs + - bring back old lock order + - remove migration v13 + - revert CURRENT_VERSION to 12 in migration.rs + - more dos protection ([#8104](https://github.com/paritytech/parity/pull/8104)) + - Const time comparison ([#8113](https://github.com/paritytech/parity/pull/8113)) + - Use `subtle::slices_equal` for constant time comparison. + - Also update the existing version of subtle in `ethcrypto` from 0.1 to 0.5 + - Test specifically for InvalidPassword error. + - fix trace filter returning returning unrelated reward calls, closes #8070 ([#8098](https://github.com/paritytech/parity/pull/8098)) + - network: init discovery using healthy nodes ([#8061](https://github.com/paritytech/parity/pull/8061)) + - network: init discovery using healthy nodes + - network: fix style grumble + - network: fix typo + - Postpone Kovan hard fork ([#8137](https://github.com/paritytech/parity/pull/8137)) + - ethcore: postpone Kovan hard fork + - util: update version fork metadata + - Disable UI by default. ([#8105](https://github.com/paritytech/parity/pull/8105)) + - dapps: update parity-ui dependencies ([#8160](https://github.com/paritytech/parity/pull/8160)) +- Probe changes one step deeper ([#8134](https://github.com/paritytech/parity/pull/8134)) ([#8135](https://github.com/paritytech/parity/pull/8135)) +- Beta backports ([#8053](https://github.com/paritytech/parity/pull/8053)) + - CI: Fix cargo cache ([#7968](https://github.com/paritytech/parity/pull/7968)) + - Fix cache + - Only clean locked cargo cache on windows + - fixed ethstore sign ([#8026](https://github.com/paritytech/parity/pull/8026)) + - fixed parsing ethash seals and verify_block_undordered ([#8031](https://github.com/paritytech/parity/pull/8031)) + - fix for verify_block_basic crashing on invalid transaction rlp ([#8032](https://github.com/paritytech/parity/pull/8032)) + - fix cache & snapcraft CI build ([#8052](https://github.com/paritytech/parity/pull/8052)) + - Add MCIP-6 Byzyantium transition to Musicoin spec ([#7841](https://github.com/paritytech/parity/pull/7841)) + - Add test chain spec for musicoin byzantium testnet + - Add MCIP-6 Byzyantium transition to Musicoin spec + - Update mcip6_byz.json + - ethcore: update musicoin byzantium block number + - ethcore: update musicoin bootnodes + - Update musicoin.json + - More bootnodes. +- Make 1.10 beta ([#8022](https://github.com/paritytech/parity/pull/8022)) + - Make 1.10 beta + - Fix gitlab builds +- SecretStore: secretstore_generateDocumentKey RPC ([#7864](https://github.com/paritytech/parity/pull/7864)) +- SecretStore: ECDSA session for cases when 2*t < N ([#7739](https://github.com/paritytech/parity/pull/7739)) +- bump tiny-keccak ([#8019](https://github.com/paritytech/parity/pull/8019)) +- Remove un-necessary comment ([#8014](https://github.com/paritytech/parity/pull/8014)) +- clean up account fmt::Debug ([#7983](https://github.com/paritytech/parity/pull/7983)) +- improve quality of vote_collector module ([#7984](https://github.com/paritytech/parity/pull/7984)) +- ExecutedBlock cleanup ([#7991](https://github.com/paritytech/parity/pull/7991)) +- Hardware-wallet/usb-subscribe-refactor ([#7860](https://github.com/paritytech/parity/pull/7860)) +- remove wildcard imports from views, make tests more idiomatic ([#7986](https://github.com/paritytech/parity/pull/7986)) +- moved PerfTimer to a separate crate - "trace-time" ([#7985](https://github.com/paritytech/parity/pull/7985)) +- clean up ethcore::spec module imports ([#7990](https://github.com/paritytech/parity/pull/7990)) +- rpc: don't include current block in new_block_filter ([#7982](https://github.com/paritytech/parity/pull/7982)) +- fix traces, removed bloomchain crate ([#7979](https://github.com/paritytech/parity/pull/7979)) +- simplify compression and move it out of rlp crate ([#7957](https://github.com/paritytech/parity/pull/7957)) +- removed old migrations ([#7974](https://github.com/paritytech/parity/pull/7974)) +- Reject too large packets in snapshot sync. ([#7977](https://github.com/paritytech/parity/pull/7977)) +- fixed broken logs ([#7934](https://github.com/paritytech/parity/pull/7934)) +- Increase max download limit to 128MB ([#7965](https://github.com/paritytech/parity/pull/7965)) +- Calculate proper keccak256/sha3 using parity. ([#7953](https://github.com/paritytech/parity/pull/7953)) +- Add changelog for 1.8.10 stable and 1.9.3 beta ([#7947](https://github.com/paritytech/parity/pull/7947)) +- kvdb-rocksdb: remove buffered operations when committing transaction ([#7950](https://github.com/paritytech/parity/pull/7950)) +- Bump WebSockets ([#7952](https://github.com/paritytech/parity/pull/7952)) +- removed redundant Bloom conversions ([#7932](https://github.com/paritytech/parity/pull/7932)) +- simplify RefInfo fmt ([#7929](https://github.com/paritytech/parity/pull/7929)) +- Kovan WASM fork code ([#7849](https://github.com/paritytech/parity/pull/7849)) +- bring back trie and triehash benches ([#7926](https://github.com/paritytech/parity/pull/7926)) +- removed redundant PodAccount::new method ([#7928](https://github.com/paritytech/parity/pull/7928)) +- removed dummy wrapper structure - LogGroupPosition ([#7922](https://github.com/paritytech/parity/pull/7922)) +- spec: Validate required divisor fields are not 0 ([#7933](https://github.com/paritytech/parity/pull/7933)) +- simplify Client::filter_traces method ([#7936](https://github.com/paritytech/parity/pull/7936)) +- gitlab cache ([#7921](https://github.com/paritytech/parity/pull/7921)) +- Fix a division by zero in light client RPC handler ([#7917](https://github.com/paritytech/parity/pull/7917)) +- triehash optimisations ([#7920](https://github.com/paritytech/parity/pull/7920)) +- removed redundant Blockchain::db method ([#7919](https://github.com/paritytech/parity/pull/7919)) +- removed redundant Blockchain::rewind method ([#7918](https://github.com/paritytech/parity/pull/7918)) +- Pending transactions subscription ([#7906](https://github.com/paritytech/parity/pull/7906)) +- removed redundant otry! macro from ethcore ([#7916](https://github.com/paritytech/parity/pull/7916)) +- Make block generator easier to use ([#7888](https://github.com/paritytech/parity/pull/7888)) +- ECIP 1041 - Remove Difficulty Bomb ([#7905](https://github.com/paritytech/parity/pull/7905)) +- Fix CSP for dapps that require eval. ([#7867](https://github.com/paritytech/parity/pull/7867)) +- Fix gitlab ([#7901](https://github.com/paritytech/parity/pull/7901)) +- Gitlb snap master patch ([#7900](https://github.com/paritytech/parity/pull/7900)) +- fix snap build master ([#7896](https://github.com/paritytech/parity/pull/7896)) +- Fix wallet import ([#7873](https://github.com/paritytech/parity/pull/7873)) +- Fix snapcraft nightly ([#7884](https://github.com/paritytech/parity/pull/7884)) +- Add a timeout for light client sync requests ([#7848](https://github.com/paritytech/parity/pull/7848)) +- SecretStore: fixed test ([#7878](https://github.com/paritytech/parity/pull/7878)) +- Fix checksums and auto-update push ([#7846](https://github.com/paritytech/parity/pull/7846)) +- Forward-port snap fixes ([#7831](https://github.com/paritytech/parity/pull/7831)) +- Update gitlab-test.sh ([#7883](https://github.com/paritytech/parity/pull/7883)) +- Fix installer binary names for macos and windows ([#7881](https://github.com/paritytech/parity/pull/7881)) +- Fix string typo: "develoopment" -> "development" ([#7874](https://github.com/paritytech/parity/pull/7874)) +- Update the instructions to install the stable snap ([#7876](https://github.com/paritytech/parity/pull/7876)) +- SecretStore: 'broadcast' decryption session ([#7843](https://github.com/paritytech/parity/pull/7843)) +- Flush keyfiles. Resolves #7632 ([#7868](https://github.com/paritytech/parity/pull/7868)) +- Read registry_address from given block ([#7866](https://github.com/paritytech/parity/pull/7866)) +- Clean up docs formatting for Wasm runtime ([#7869](https://github.com/paritytech/parity/pull/7869)) +- WASM: Disable internal memory ([#7842](https://github.com/paritytech/parity/pull/7842)) +- Update gitlab-build.sh ([#7855](https://github.com/paritytech/parity/pull/7855)) +- ethabi version 5 ([#7723](https://github.com/paritytech/parity/pull/7723)) +- Light client: randomize the peer we dispatch requests to ([#7844](https://github.com/paritytech/parity/pull/7844)) +- Store updater metadata in a single place ([#7832](https://github.com/paritytech/parity/pull/7832)) +- Add new EF ropstens nodes. ([#7824](https://github.com/paritytech/parity/pull/7824)) +- refactor stratum to remove retain cycle ([#7827](https://github.com/paritytech/parity/pull/7827)) +- Bump jsonrpc. ([#7828](https://github.com/paritytech/parity/pull/7828)) +- Add binary identifiers and sha256sum to builds ([#7830](https://github.com/paritytech/parity/pull/7830)) +- Update references to UI shell & wallet ([#7808](https://github.com/paritytech/parity/pull/7808)) +- Adjust storage update evm-style ([#7812](https://github.com/paritytech/parity/pull/7812)) +- Updated WASM Runtime & new interpreter (wasmi) ([#7796](https://github.com/paritytech/parity/pull/7796)) +- SecretStore: ignore removed authorities when running auto-migration ([#7674](https://github.com/paritytech/parity/pull/7674)) +- Fix build ([#7807](https://github.com/paritytech/parity/pull/7807)) +- Move js & js-old code to github.com/parity-js ([#7685](https://github.com/paritytech/parity/pull/7685)) +- More changelogs :) ([#7782](https://github.com/paritytech/parity/pull/7782)) +- Actualized API set in help ([#7790](https://github.com/paritytech/parity/pull/7790)) +- Removed obsolete file ([#7788](https://github.com/paritytech/parity/pull/7788)) +- Update ropsten bootnodes ([#7776](https://github.com/paritytech/parity/pull/7776)) +- CHANGELOG for 1.9.1 and 1.8.8 ([#7775](https://github.com/paritytech/parity/pull/7775)) +- Enable byzantium features on non-ethash chains ([#7753](https://github.com/paritytech/parity/pull/7753)) +- Fix client not being dropped on shutdown ([#7695](https://github.com/paritytech/parity/pull/7695)) +- Filter-out nodes.json ([#7716](https://github.com/paritytech/parity/pull/7716)) +- Removes redundant parentheses ([#7721](https://github.com/paritytech/parity/pull/7721)) +- Transaction-pool fixes ([#7741](https://github.com/paritytech/parity/pull/7741)) +- More visible download link in README.md ([#7707](https://github.com/paritytech/parity/pull/7707)) +- Changelog for 1.9.0 ([#7664](https://github.com/paritytech/parity/pull/7664)) +- Add scroll when too many accounts ([#7677](https://github.com/paritytech/parity/pull/7677)) +- SecretStore: return HTTP 403 (access denied) if consensus is unreachable ([#7656](https://github.com/paritytech/parity/pull/7656)) +- Moved StopGaurd to it's own crate ([#7635](https://github.com/paritytech/parity/pull/7635)) + +## Previous releases + +- [CHANGELOG-1.9](docs/CHANGELOG-1.9.md) (_stable_) +- [CHANGELOG-1.8](docs/CHANGELOG-1.8.md) (EOL: 2018-03-22) +- [CHANGELOG-1.7](docs/CHANGELOG-1.7.md) (EOL: 2018-01-25) +- [CHANGELOG-1.6](docs/CHANGELOG-1.6.md) (EOL: 2017-10-15) +- [CHANGELOG-1.5](docs/CHANGELOG-1.5.md) (EOL: 2017-07-28) +- [CHANGELOG-1.4](docs/CHANGELOG-1.4.md) (EOL: 2017-03-13) +- [CHANGELOG-1.3](docs/CHANGELOG-1.3.md) (EOL: 2017-01-19) +- [CHANGELOG-1.2](docs/CHANGELOG-1.2.md) (EOL: 2016-11-07) +- [CHANGELOG-1.1](docs/CHANGELOG-1.1.md) (EOL: 2016-08-12) +- [CHANGELOG-1.0](docs/CHANGELOG-1.0.md) (EOL: 2016-06-24) +- [CHANGELOG-0.9](docs/CHANGELOG-0.9.md) (EOL: 2016-05-02) diff --git a/docs/CHANGELOG-1.9.md b/docs/CHANGELOG-1.9.md index ddb7e9cee1c22c6cf3a8c227439e67fc457a7932..2be3fcf27b2833cd04a304fb6c5b6ea8191e58a4 100644 --- a/docs/CHANGELOG-1.9.md +++ b/docs/CHANGELOG-1.9.md @@ -1,3 +1,58 @@ +Note: Parity 1.9 reached End-of-Life on 2018-05-09 (EOL). + +## Parity [v1.9.7](https://github.com/paritytech/parity/releases/tag/v1.9.7) (2018-04-23) + +Parity 1.9.7 is a bug-fix release to improve performance and stability. + +The full list of included changes: + +- Update Parity stable to 1.9.7 + Backports ([#8456](https://github.com/paritytech/parity/pull/8456)) + - Update Parity stable to 1.9.7 + - Allow 32-bit pipelines to fail ([#8454](https://github.com/paritytech/parity/pull/8454)) + - Disable 32-bit targets for Gitlab + - Rename Linux pipelines + - Update wasmi ([#8452](https://github.com/paritytech/parity/pull/8452)) + - Revert Cargo lock update from master + - Fix Cargo.lock +- Backports ([#8449](https://github.com/paritytech/parity/pull/8449)) + - Use forked app_dirs crate for reverted Windows dir behavior ([#8438](https://github.com/paritytech/parity/pull/8438)) + - Remove unused app_dirs dependency in CLI + - Use forked app_dirs crate for reverted Windows dir behavior + - Remove Tendermint extra_info due to seal inconsistencies ([#8367](https://github.com/paritytech/parity/pull/8367)) + - Improve VM executor stack size estimation rules ([#8439](https://github.com/paritytech/parity/pull/8439)) + - Improve VM executor stack size estimation rules + - Typo: docs add "(Debug build)" comment + - Fix an off by one typo and set minimal stack size + - Use saturating_sub to avoid potential overflow + - Upgrade crossbeam to 0.3 + +## Parity [v1.9.6](https://github.com/paritytech/parity/releases/tag/v1.9.6) (2018-04-16) + +Parity 1.9.6 is a bug-fix release to improve performance and stability. + +The full list of included changes: + +- Bump app_dirs, fixes [#8315](https://github.com/paritytech/parity/issues/8315) ([#8355](https://github.com/paritytech/parity/pull/8355)) +- Fix Cargo lock +- Backports ([#8352](https://github.com/paritytech/parity/pull/8352)) + - Update musicoin spec in line with gmc v2.6.2 ([#8242](https://github.com/paritytech/parity/pull/8242)) + - Supress TemporaryInvalid verification failures. ([#8256](https://github.com/paritytech/parity/pull/8256)) + - Include suicided accounts in state diff ([#8297](https://github.com/paritytech/parity/pull/8297)) + - Include suicided accounts in state diff + - Shorten form match -> if let + - Test suicide trace diff in State + - Replace_home for password_files, reserved_peers and log_file ([#8324](https://github.com/paritytech/parity/pull/8324)) + - Replace_home for password_files, reserved_peers and log_file + - Typo: arg_log_file is Option + - Bump version in util/version +- Bump stable to 1.9.6 ([#8348](https://github.com/paritytech/parity/pull/8348)) +- WASM libraries bump ([#8219](https://github.com/paritytech/parity/pull/8219)) + - Bump wasm libs ([#8171](https://github.com/paritytech/parity/pull/8171)) + - Bump wasmi version ([#8209](https://github.com/paritytech/parity/pull/8209)) +- Updated jsonrpc to include latest backports (1.9) ([#8182](https://github.com/paritytech/parity/pull/8182)) + - Updated jsonrpc to include latest backports (1.9) + - Update dependencies. + ## Parity [v1.9.5](https://github.com/paritytech/parity/releases/tag/v1.9.5) (2018-03-21) Parity 1.9.5 is a bug-fix release to improve performance and stability. This release marks the 1.9 track _stable_. diff --git a/ethash/Cargo.toml b/ethash/Cargo.toml index 91f1ce56f700aba239fa43b86bc7f1d99ec38baf..4bb1b50470ed9de363c7126833b225b8d03306e1 100644 --- a/ethash/Cargo.toml +++ b/ethash/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ethash" -version = "1.11.0" +version = "1.12.0" authors = ["Parity Technologies "] [lib] diff --git a/ethash/src/cache.rs b/ethash/src/cache.rs index eef426bcf1b95ad7fe5904f4689ab3d90c42a9e3..21bd0e231ef30f97a15aa7f90804c57240fc30c3 100644 --- a/ethash/src/cache.rs +++ b/ethash/src/cache.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethash/src/compute.rs b/ethash/src/compute.rs index 48906b9edeb6b9565850631f529becf08dd5f840..fa95038d1859d5d3e2afdcb77b46b0ee18976df2 100644 --- a/ethash/src/compute.rs +++ b/ethash/src/compute.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -25,9 +25,8 @@ use seed_compute::SeedHashCompute; use shared::*; use std::io; -use std::mem; +use std::{mem, ptr}; use std::path::Path; -use std::ptr; const MIX_WORDS: usize = ETHASH_MIX_BYTES / 4; const MIX_NODES: usize = MIX_WORDS / NODE_WORDS; @@ -111,7 +110,7 @@ pub fn quick_get_difficulty(header_hash: &H256, nonce: u64, mix_hash: &H256) -> let mut buf: [u8; 64 + 32] = mem::uninitialized(); ptr::copy_nonoverlapping(header_hash.as_ptr(), buf.as_mut_ptr(), 32); - ptr::copy_nonoverlapping(mem::transmute(&nonce), buf[32..].as_mut_ptr(), 8); + ptr::copy_nonoverlapping(&nonce as *const u64 as *const u8, buf[32..].as_mut_ptr(), 8); keccak_512::unchecked(buf.as_mut_ptr(), 64, buf.as_ptr(), 40); ptr::copy_nonoverlapping(mix_hash.as_ptr(), buf[64..].as_mut_ptr(), 32); diff --git a/ethash/src/keccak.rs b/ethash/src/keccak.rs index 8ca4f543844362f13e27a73ea3efb6889f28d257..ab6be94dcaac31519102381a636696f679645eca 100644 --- a/ethash/src/keccak.rs +++ b/ethash/src/keccak.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -21,32 +21,36 @@ pub type H256 = [u8; 32]; pub mod keccak_512 { use super::hash; - pub use self::hash::keccak_512 as unchecked; + pub use self::hash::keccak_512_unchecked as unchecked; pub fn write(input: &[u8], output: &mut [u8]) { - unsafe { hash::keccak_512(output.as_mut_ptr(), output.len(), input.as_ptr(), input.len()) }; + hash::keccak_512(input, output); } pub fn inplace(input: &mut [u8]) { - // This is safe since `sha3_*` uses an internal buffer and copies the result to the output. This + // This is safe since `keccak_*` uses an internal buffer and copies the result to the output. This // means that we can reuse the input buffer for both input and output. - unsafe { hash::keccak_512(input.as_mut_ptr(), input.len(), input.as_ptr(), input.len()) }; + unsafe { + hash::keccak_512_unchecked(input.as_mut_ptr(), input.len(), input.as_ptr(), input.len()); + } } } pub mod keccak_256 { use super::hash; - pub use self::hash::keccak_256 as unchecked; + pub use self::hash::keccak_256_unchecked as unchecked; #[allow(dead_code)] pub fn write(input: &[u8], output: &mut [u8]) { - unsafe { hash::keccak_256(output.as_mut_ptr(), output.len(), input.as_ptr(), input.len()) }; + hash::keccak_256(input, output); } pub fn inplace(input: &mut [u8]) { - // This is safe since `sha3_*` uses an internal buffer and copies the result to the output. This + // This is safe since `keccak_*` uses an internal buffer and copies the result to the output. This // means that we can reuse the input buffer for both input and output. - unsafe { hash::keccak_256(input.as_mut_ptr(), input.len(), input.as_ptr(), input.len()) }; + unsafe { + hash::keccak_256_unchecked(input.as_mut_ptr(), input.len(), input.as_ptr(), input.len()); + } } } diff --git a/ethash/src/lib.rs b/ethash/src/lib.rs index 9d0c669d9c8c7454774d132699b4196d3a31b915..69b5a1d11551d5ac1815c0f02a45ab6ce67bc169 100644 --- a/ethash/src/lib.rs +++ b/ethash/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethash/src/seed_compute.rs b/ethash/src/seed_compute.rs index 04774b3e39e507609c54db43ee1bccd0ca972e8a..bc6f1d51e1bfa0447a04a243fe7c61e0c28dc09b 100644 --- a/ethash/src/seed_compute.rs +++ b/ethash/src/seed_compute.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethash/src/shared.rs b/ethash/src/shared.rs index 39e1c8eb88479513b64de007d8b8ed4b2e76b7bf..90969c522254a48fd2dcbee3a5942fee7f30229b 100644 --- a/ethash/src/shared.rs +++ b/ethash/src/shared.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/Cargo.toml b/ethcore/Cargo.toml index 065bbc5cc5999ca4fc3c3937d3f669d4eb4896f9..2b6ab9ebc3a869e3f3683f7e142468f003a42f43 100644 --- a/ethcore/Cargo.toml +++ b/ethcore/Cargo.toml @@ -3,7 +3,7 @@ description = "Ethcore library" homepage = "http://parity.io" license = "GPL-3.0" name = "ethcore" -version = "1.11.0" +version = "1.12.0" authors = ["Parity Technologies "] [dependencies] @@ -16,15 +16,18 @@ crossbeam = "0.3" ethash = { path = "../ethash" } ethcore-bloom-journal = { path = "../util/bloom" } ethcore-bytes = { path = "../util/bytes" } +fetch = { path = "../util/fetch" } hashdb = { path = "../util/hashdb" } memorydb = { path = "../util/memorydb" } patricia-trie = { path = "../util/patricia_trie" } +ethcore-crypto = { path = "crypto" } +error-chain = { version = "0.11", default-features = false } ethcore-io = { path = "../util/io" } ethcore-logger = { path = "../logger" } ethcore-miner = { path = "../miner" } -ethcore-stratum = { path = "../stratum" } +ethcore-stratum = { path = "./stratum" } ethcore-transaction = { path = "./transaction" } -ethereum-types = "0.2" +ethereum-types = "0.3" memory-cache = { path = "../util/memory_cache" } ethabi = "5.1" ethabi-derive = "5.0" @@ -33,7 +36,6 @@ ethjson = { path = "../json" } ethkey = { path = "../ethkey" } ethstore = { path = "../ethstore" } evm = { path = "evm" } -futures-cpupool = "0.1" hardware-wallet = { path = "../hw" } heapsize = "0.4" itertools = "0.5" @@ -44,26 +46,21 @@ num = { version = "0.1", default-features = false, features = ["bigint"] } num_cpus = "1.2" parity-machine = { path = "../machine" } parking_lot = "0.5" -price-info = { path = "../price-info" } -rayon = "0.8" +rayon = "1.0" rand = "0.4" rlp = { path = "../util/rlp" } rlp_compress = { path = "../util/rlp_compress" } rlp_derive = { path = "../util/rlp_derive" } kvdb = { path = "../util/kvdb" } -kvdb-rocksdb = { path = "../util/kvdb-rocksdb" } kvdb-memorydb = { path = "../util/kvdb-memorydb" } util-error = { path = "../util/error" } snappy = { git = "https://github.com/paritytech/rust-snappy" } stop-guard = { path = "../util/stop-guard" } -migration = { path = "../util/migration" } macros = { path = "../util/macros" } -rust-crypto = "0.2.34" rustc-hex = "1.0" stats = { path = "../util/stats" } trace-time = { path = "../util/trace-time" } using_queue = { path = "../util/using_queue" } -table = { path = "../util/table" } vm = { path = "vm" } wasm = { path = "wasm" } keccak-hash = { path = "../util/hash" } @@ -74,13 +71,23 @@ journaldb = { path = "../util/journaldb" } [dev-dependencies] tempdir = "0.3" trie-standardmap = { path = "../util/trie-standardmap" } +kvdb-rocksdb = { path = "../util/kvdb-rocksdb" } [features] -jit = ["evm/jit"] +# Display EVM debug traces. evm-debug = ["slow-blocks"] +# Display EVM debug traces when running tests. evm-debug-tests = ["evm-debug", "evm/evm-debug-tests"] -slow-blocks = [] # Use SLOW_TX_DURATION="50" (compile time!) to track transactions over 50ms +# Measure time of transaction execution. +# Whenever the transaction execution time (in millis) exceeds the value of +# SLOW_TX_DURATION env variable (provided compile time!) +# EVM debug traces are printed. +slow-blocks = [] +# Run JSON consensus tests. json-tests = ["ethcore-transaction/json-tests"] +# Run memory/cpu heavy tests. test-heavy = [] -default = [] +# Compile benches benches = [] +# Compile test helpers +test-helpers = [] diff --git a/ethcore/benches/evm.rs b/ethcore/benches/evm.rs index 324e3382e57603b6be8706fef472d1c904595d42..c68adc98787aa32387029e33521869c59660f8fd 100644 --- a/ethcore/benches/evm.rs +++ b/ethcore/benches/evm.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ extern crate test; extern crate ethcore_util as util; extern crate rand; extern crate bn; -extern crate crypto; +extern crate ethcore_crypto; extern crate ethkey; extern crate rustc_hex; extern crate ethcore_bigint; @@ -28,7 +28,6 @@ extern crate ethcore_bigint; use self::test::{Bencher}; use rand::{StdRng}; - #[bench] fn bn_128_pairing(b: &mut Bencher) { use bn::{pairing, G1, G2, Fr, Group}; @@ -61,16 +60,13 @@ fn bn_128_mul(b: &mut Bencher) { #[bench] fn sha256(b: &mut Bencher) { - use crypto::sha2::Sha256; - use crypto::digest::Digest; + use ethcore_crypto::digest::sha256; let mut input: [u8; 256] = [0; 256]; let mut out = [0; 32]; b.iter(|| { - let mut sha = Sha256::new(); - sha.input(&input); - sha.result(&mut input[0..32]); + sha256(&input); }); } @@ -95,4 +91,3 @@ fn ecrecover(b: &mut Bencher) { let _ = ec_recover(&s, &hash); }); } - diff --git a/ethcore/crypto/Cargo.toml b/ethcore/crypto/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..b57b8497c0ed168ad97130e4a40645f811013a1d --- /dev/null +++ b/ethcore/crypto/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "ethcore-crypto" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +ethereum-types = "0.3" +quick-error = "1.2" +ring = "0.12" +rust-crypto = "0.2.36" +tiny-keccak = "1.4" + diff --git a/ethcrypto/README.md b/ethcore/crypto/README.md similarity index 100% rename from ethcrypto/README.md rename to ethcore/crypto/README.md diff --git a/ethcore/crypto/src/aes.rs b/ethcore/crypto/src/aes.rs new file mode 100644 index 0000000000000000000000000000000000000000..42a26fad0dcd845158fff2b36fa5a4d2232cdcb2 --- /dev/null +++ b/ethcore/crypto/src/aes.rs @@ -0,0 +1,53 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use error::SymmError; +use rcrypto::blockmodes::{CtrMode, CbcDecryptor, PkcsPadding}; +use rcrypto::aessafe::{AesSafe128Encryptor, AesSafe128Decryptor}; +use rcrypto::symmetriccipher::{Encryptor, Decryptor}; +use rcrypto::buffer::{RefReadBuffer, RefWriteBuffer, WriteBuffer}; + +/// Encrypt a message (CTR mode). +/// +/// Key (`k`) length and initialisation vector (`iv`) length have to be 16 bytes each. +/// An error is returned if the input lengths are invalid. +pub fn encrypt_128_ctr(k: &[u8], iv: &[u8], plain: &[u8], dest: &mut [u8]) -> Result<(), SymmError> { + let mut encryptor = CtrMode::new(AesSafe128Encryptor::new(k), iv.to_vec()); + encryptor.encrypt(&mut RefReadBuffer::new(plain), &mut RefWriteBuffer::new(dest), true)?; + Ok(()) +} + +/// Decrypt a message (CTR mode). +/// +/// Key (`k`) length and initialisation vector (`iv`) length have to be 16 bytes each. +/// An error is returned if the input lengths are invalid. +pub fn decrypt_128_ctr(k: &[u8], iv: &[u8], encrypted: &[u8], dest: &mut [u8]) -> Result<(), SymmError> { + let mut encryptor = CtrMode::new(AesSafe128Encryptor::new(k), iv.to_vec()); + encryptor.decrypt(&mut RefReadBuffer::new(encrypted), &mut RefWriteBuffer::new(dest), true)?; + Ok(()) +} + +/// Decrypt a message (CBC mode). +/// +/// Key (`k`) length and initialisation vector (`iv`) length have to be 16 bytes each. +/// An error is returned if the input lengths are invalid. +pub fn decrypt_128_cbc(k: &[u8], iv: &[u8], encrypted: &[u8], dest: &mut [u8]) -> Result { + let mut encryptor = CbcDecryptor::new(AesSafe128Decryptor::new(k), PkcsPadding, iv.to_vec()); + let len = dest.len(); + let mut buffer = RefWriteBuffer::new(dest); + encryptor.decrypt(&mut RefReadBuffer::new(encrypted), &mut buffer, true)?; + Ok(len - buffer.remaining()) +} diff --git a/ethcore/crypto/src/aes_gcm.rs b/ethcore/crypto/src/aes_gcm.rs new file mode 100644 index 0000000000000000000000000000000000000000..819c613197d3d036668162447f02e51f1a64a23b --- /dev/null +++ b/ethcore/crypto/src/aes_gcm.rs @@ -0,0 +1,198 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use error::SymmError; +use ring; + +enum Mode { Aes128Gcm, Aes256Gcm } + +/// AES GCM encryptor. +pub struct Encryptor<'a> { + mode: Mode, + key: ring::aead::SealingKey, + ad: &'a [u8], + offset: usize, +} + +impl<'a> Encryptor<'a> { + pub fn aes_128_gcm(key: &[u8; 16]) -> Result, SymmError> { + let sk = ring::aead::SealingKey::new(&ring::aead::AES_128_GCM, key)?; + Ok(Encryptor { + mode: Mode::Aes128Gcm, + key: sk, + ad: &[], + offset: 0, + }) + } + + pub fn aes_256_gcm(key: &[u8; 32]) -> Result, SymmError> { + let sk = ring::aead::SealingKey::new(&ring::aead::AES_256_GCM, key)?; + Ok(Encryptor { + mode: Mode::Aes256Gcm, + key: sk, + ad: &[], + offset: 0, + }) + } + + /// Optional associated data which is not encrypted but authenticated. + pub fn associate(&mut self, data: &'a [u8]) -> &mut Self { + self.ad = data; + self + } + + /// Optional offset value. Only the slice `[offset..]` will be encrypted. + pub fn offset(&mut self, off: usize) -> &mut Self { + self.offset = off; + self + } + + /// Please note that the pair (key, nonce) must never be reused. Using random nonces + /// limits the number of messages encrypted with the same key to 2^32 (cf. [[1]]) + /// + /// [1]: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf + pub fn encrypt(&self, nonce: &[u8; 12], mut data: Vec) -> Result, SymmError> { + if self.offset > data.len() { + return Err(SymmError::offset_error(self.offset)) + } + let tag_len = match self.mode { + Mode::Aes128Gcm => ring::aead::AES_128_GCM.tag_len(), + Mode::Aes256Gcm => ring::aead::AES_256_GCM.tag_len(), + }; + data.extend(::std::iter::repeat(0).take(tag_len)); + let len = ring::aead::seal_in_place(&self.key, nonce, self.ad, &mut data[self.offset ..], tag_len)?; + data.truncate(self.offset + len); + Ok(data) + } +} + +/// AES GCM decryptor. +pub struct Decryptor<'a> { + key: ring::aead::OpeningKey, + ad: &'a [u8], + offset: usize, +} + +impl<'a> Decryptor<'a> { + pub fn aes_128_gcm(key: &[u8; 16]) -> Result, SymmError> { + let ok = ring::aead::OpeningKey::new(&ring::aead::AES_128_GCM, key)?; + Ok(Decryptor { + key: ok, + ad: &[], + offset: 0, + }) + } + + pub fn aes_256_gcm(key: &[u8; 32]) -> Result, SymmError> { + let ok = ring::aead::OpeningKey::new(&ring::aead::AES_256_GCM, key)?; + Ok(Decryptor { + key: ok, + ad: &[], + offset: 0, + }) + } + + /// Optional associated data which is not encrypted but authenticated. + pub fn associate(&mut self, data: &'a [u8]) -> &mut Self { + self.ad = data; + self + } + + /// Optional offset value. Only the slice `[offset..]` will be decrypted. + pub fn offset(&mut self, off: usize) -> &mut Self { + self.offset = off; + self + } + + pub fn decrypt(&self, nonce: &[u8; 12], mut data: Vec) -> Result, SymmError> { + if self.offset > data.len() { + return Err(SymmError::offset_error(self.offset)) + } + let len = ring::aead::open_in_place(&self.key, nonce, self.ad, 0, &mut data[self.offset ..])?.len(); + data.truncate(self.offset + len); + Ok(data) + } +} + +#[cfg(test)] +mod tests { + use super::{Encryptor, Decryptor}; + + #[test] + fn aes_gcm_128() { + let secret = b"1234567890123456"; + let nonce = b"123456789012"; + let message = b"So many books, so little time"; + + let ciphertext = Encryptor::aes_128_gcm(secret) + .unwrap() + .encrypt(nonce, message.to_vec()) + .unwrap(); + + assert!(ciphertext != message); + + let plaintext = Decryptor::aes_128_gcm(secret) + .unwrap() + .decrypt(nonce, ciphertext) + .unwrap(); + + assert_eq!(plaintext, message) + } + + #[test] + fn aes_gcm_256() { + let secret = b"12345678901234567890123456789012"; + let nonce = b"123456789012"; + let message = b"So many books, so little time"; + + let ciphertext = Encryptor::aes_256_gcm(secret) + .unwrap() + .encrypt(nonce, message.to_vec()) + .unwrap(); + + assert!(ciphertext != message); + + let plaintext = Decryptor::aes_256_gcm(secret) + .unwrap() + .decrypt(nonce, ciphertext) + .unwrap(); + + assert_eq!(plaintext, message) + } + + #[test] + fn aes_gcm_256_offset() { + let secret = b"12345678901234567890123456789012"; + let nonce = b"123456789012"; + let message = b"prefix data; So many books, so little time"; + + let ciphertext = Encryptor::aes_256_gcm(secret) + .unwrap() + .offset(13) // length of "prefix data; " + .encrypt(nonce, message.to_vec()) + .unwrap(); + + assert!(ciphertext != &message[..]); + + let plaintext = Decryptor::aes_256_gcm(secret) + .unwrap() + .offset(13) // length of "prefix data; " + .decrypt(nonce, ciphertext) + .unwrap(); + + assert_eq!(plaintext, &message[..]) + } +} diff --git a/ethcore/crypto/src/digest.rs b/ethcore/crypto/src/digest.rs new file mode 100644 index 0000000000000000000000000000000000000000..b2be0b8ed177fed7c652f859b98b141d28be9337 --- /dev/null +++ b/ethcore/crypto/src/digest.rs @@ -0,0 +1,109 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use rcrypto::ripemd160; +use ring::digest::{self, Context, SHA256, SHA512}; +use std::marker::PhantomData; +use std::ops::Deref; + +/// The message digest. +pub struct Digest(InnerDigest, PhantomData); + +enum InnerDigest { + Ring(digest::Digest), + Ripemd160([u8; 20]), +} + +impl Deref for Digest { + type Target = [u8]; + fn deref(&self) -> &Self::Target { + match self.0 { + InnerDigest::Ring(ref d) => d.as_ref(), + InnerDigest::Ripemd160(ref d) => &d[..] + } + } +} + +/// Single-step sha256 digest computation. +pub fn sha256(data: &[u8]) -> Digest { + Digest(InnerDigest::Ring(digest::digest(&SHA256, data)), PhantomData) +} + +/// Single-step sha512 digest computation. +pub fn sha512(data: &[u8]) -> Digest { + Digest(InnerDigest::Ring(digest::digest(&SHA512, data)), PhantomData) +} + +/// Single-step ripemd160 digest computation. +pub fn ripemd160(data: &[u8]) -> Digest { + let mut hasher = Hasher::ripemd160(); + hasher.update(data); + hasher.finish() +} + +pub enum Sha256 {} +pub enum Sha512 {} +pub enum Ripemd160 {} + +/// Stateful digest computation. +pub struct Hasher(Inner, PhantomData); + +enum Inner { + Ring(Context), + Ripemd160(ripemd160::Ripemd160) +} + +impl Hasher { + pub fn sha256() -> Hasher { + Hasher(Inner::Ring(Context::new(&SHA256)), PhantomData) + } +} + +impl Hasher { + pub fn sha512() -> Hasher { + Hasher(Inner::Ring(Context::new(&SHA512)), PhantomData) + } +} + +impl Hasher { + pub fn ripemd160() -> Hasher { + Hasher(Inner::Ripemd160(ripemd160::Ripemd160::new()), PhantomData) + } +} + +impl Hasher { + pub fn update(&mut self, data: &[u8]) { + match self.0 { + Inner::Ring(ref mut ctx) => ctx.update(data), + Inner::Ripemd160(ref mut ctx) => { + use rcrypto::digest::Digest; + ctx.input(data) + } + } + } + + pub fn finish(self) -> Digest { + match self.0 { + Inner::Ring(ctx) => Digest(InnerDigest::Ring(ctx.finish()), PhantomData), + Inner::Ripemd160(mut ctx) => { + use rcrypto::digest::Digest; + let mut d = [0; 20]; + ctx.result(&mut d); + Digest(InnerDigest::Ripemd160(d), PhantomData) + } + } + } +} diff --git a/ethcore/crypto/src/error.rs b/ethcore/crypto/src/error.rs new file mode 100644 index 0000000000000000000000000000000000000000..4e5582e196cda0bfc6c09e8a2d662d6867c58b40 --- /dev/null +++ b/ethcore/crypto/src/error.rs @@ -0,0 +1,82 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use rcrypto; +use ring; + +quick_error! { + #[derive(Debug)] + pub enum Error { + Scrypt(e: ScryptError) { + cause(e) + from() + } + Symm(e: SymmError) { + cause(e) + from() + } + } +} + +quick_error! { + #[derive(Debug)] + pub enum ScryptError { + // log(N) < r / 16 + InvalidN { + display("Invalid N argument of the scrypt encryption") + } + // p <= (2^31-1 * 32)/(128 * r) + InvalidP { + display("Invalid p argument of the scrypt encryption") + } + } +} + +quick_error! { + #[derive(Debug)] + pub enum SymmError wraps PrivSymmErr { + RustCrypto(e: rcrypto::symmetriccipher::SymmetricCipherError) { + display("symmetric crypto error") + from() + } + Ring(e: ring::error::Unspecified) { + display("symmetric crypto error") + cause(e) + from() + } + Offset(x: usize) { + display("offset {} greater than slice length", x) + } + } +} + +impl SymmError { + pub(crate) fn offset_error(x: usize) -> SymmError { + SymmError(PrivSymmErr::Offset(x)) + } +} + +impl From for SymmError { + fn from(e: ring::error::Unspecified) -> SymmError { + SymmError(PrivSymmErr::Ring(e)) + } +} + +impl From for SymmError { + fn from(e: rcrypto::symmetriccipher::SymmetricCipherError) -> SymmError { + SymmError(PrivSymmErr::RustCrypto(e)) + } +} diff --git a/ethcore/crypto/src/hmac.rs b/ethcore/crypto/src/hmac.rs new file mode 100644 index 0000000000000000000000000000000000000000..ff337ed02448004e201a752228919480fa0376dd --- /dev/null +++ b/ethcore/crypto/src/hmac.rs @@ -0,0 +1,88 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use digest; +use ring::digest::{SHA256, SHA512}; +use ring::hmac::{self, SigningContext}; +use std::marker::PhantomData; +use std::ops::Deref; + +/// HMAC signature. +pub struct Signature(hmac::Signature, PhantomData); + +impl Deref for Signature { + type Target = [u8]; + fn deref(&self) -> &Self::Target { + self.0.as_ref() + } +} + +/// HMAC signing key. +pub struct SigKey(hmac::SigningKey, PhantomData); + +impl SigKey { + pub fn sha256(key: &[u8]) -> SigKey { + SigKey(hmac::SigningKey::new(&SHA256, key), PhantomData) + } +} + +impl SigKey { + pub fn sha512(key: &[u8]) -> SigKey { + SigKey(hmac::SigningKey::new(&SHA512, key), PhantomData) + } +} + +/// Compute HMAC signature of `data`. +pub fn sign(k: &SigKey, data: &[u8]) -> Signature { + Signature(hmac::sign(&k.0, data), PhantomData) +} + +/// Stateful HMAC computation. +pub struct Signer(SigningContext, PhantomData); + +impl Signer { + pub fn with(key: &SigKey) -> Signer { + Signer(hmac::SigningContext::with_key(&key.0), PhantomData) + } + + pub fn update(&mut self, data: &[u8]) { + self.0.update(data) + } + + pub fn sign(self) -> Signature { + Signature(self.0.sign(), PhantomData) + } +} + +/// HMAC signature verification key. +pub struct VerifyKey(hmac::VerificationKey, PhantomData); + +impl VerifyKey { + pub fn sha256(key: &[u8]) -> VerifyKey { + VerifyKey(hmac::VerificationKey::new(&SHA256, key), PhantomData) + } +} + +impl VerifyKey { + pub fn sha512(key: &[u8]) -> VerifyKey { + VerifyKey(hmac::VerificationKey::new(&SHA512, key), PhantomData) + } +} + +/// Verify HMAC signature of `data`. +pub fn verify(k: &VerifyKey, data: &[u8], sig: &[u8]) -> bool { + hmac::verify(&k.0, data, sig).is_ok() +} diff --git a/ethcore/crypto/src/lib.rs b/ethcore/crypto/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..459c790319f7a7498285a97bae3151dbed5b0b02 --- /dev/null +++ b/ethcore/crypto/src/lib.rs @@ -0,0 +1,76 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Crypto utils used ethstore and network. + +extern crate crypto as rcrypto; +extern crate ethereum_types; +#[macro_use] +extern crate quick_error; +extern crate ring; +extern crate tiny_keccak; + +pub mod aes; +pub mod aes_gcm; +pub mod error; +pub mod scrypt; +pub mod digest; +pub mod hmac; +pub mod pbkdf2; + +pub use error::Error; + +use tiny_keccak::Keccak; + +pub const KEY_LENGTH: usize = 32; +pub const KEY_ITERATIONS: usize = 10240; +pub const KEY_LENGTH_AES: usize = KEY_LENGTH / 2; + +/// Default authenticated data to use (in RPC). +pub const DEFAULT_MAC: [u8; 2] = [0, 0]; + +pub trait Keccak256 { + fn keccak256(&self) -> T where T: Sized; +} + +impl Keccak256<[u8; 32]> for T where T: AsRef<[u8]> { + fn keccak256(&self) -> [u8; 32] { + let mut keccak = Keccak::new_keccak256(); + let mut result = [0u8; 32]; + keccak.update(self.as_ref()); + keccak.finalize(&mut result); + result + } +} + +pub fn derive_key_iterations(password: &str, salt: &[u8; 32], c: u32) -> (Vec, Vec) { + let mut derived_key = [0u8; KEY_LENGTH]; + pbkdf2::sha256(c, pbkdf2::Salt(salt), pbkdf2::Secret(password.as_bytes()), &mut derived_key); + let derived_right_bits = &derived_key[0..KEY_LENGTH_AES]; + let derived_left_bits = &derived_key[KEY_LENGTH_AES..KEY_LENGTH]; + (derived_right_bits.to_vec(), derived_left_bits.to_vec()) +} + +pub fn derive_mac(derived_left_bits: &[u8], cipher_text: &[u8]) -> Vec { + let mut mac = vec![0u8; KEY_LENGTH_AES + cipher_text.len()]; + mac[0..KEY_LENGTH_AES].copy_from_slice(derived_left_bits); + mac[KEY_LENGTH_AES..cipher_text.len() + KEY_LENGTH_AES].copy_from_slice(cipher_text); + mac +} + +pub fn is_equal(a: &[u8], b: &[u8]) -> bool { + ring::constant_time::verify_slices_are_equal(a, b).is_ok() +} diff --git a/ethcore/crypto/src/pbkdf2.rs b/ethcore/crypto/src/pbkdf2.rs new file mode 100644 index 0000000000000000000000000000000000000000..d210f6f659e644a397def420f8b779cd4373b77b --- /dev/null +++ b/ethcore/crypto/src/pbkdf2.rs @@ -0,0 +1,28 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use ring; + +pub struct Salt<'a>(pub &'a [u8]); +pub struct Secret<'a>(pub &'a [u8]); + +pub fn sha256(iter: u32, salt: Salt, sec: Secret, out: &mut [u8; 32]) { + ring::pbkdf2::derive(&ring::digest::SHA256, iter, salt.0, sec.0, &mut out[..]) +} + +pub fn sha512(iter: u32, salt: Salt, sec: Secret, out: &mut [u8; 64]) { + ring::pbkdf2::derive(&ring::digest::SHA512, iter, salt.0, sec.0, &mut out[..]) +} diff --git a/ethcore/crypto/src/scrypt.rs b/ethcore/crypto/src/scrypt.rs new file mode 100644 index 0000000000000000000000000000000000000000..de3cd55553e89b55ae9ea1406854a0243a68aa1d --- /dev/null +++ b/ethcore/crypto/src/scrypt.rs @@ -0,0 +1,38 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use error::ScryptError; +use rcrypto::scrypt::{scrypt, ScryptParams}; +use super::{KEY_LENGTH_AES, KEY_LENGTH}; + +pub fn derive_key(pass: &str, salt: &[u8; 32], n: u32, p: u32, r: u32) -> Result<(Vec, Vec), ScryptError> { + // sanity checks + let log_n = (32 - n.leading_zeros() - 1) as u8; + if log_n as u32 >= r * 16 { + return Err(ScryptError::InvalidN); + } + + if p as u64 > ((u32::max_value() as u64 - 1) * 32)/(128 * (r as u64)) { + return Err(ScryptError::InvalidP); + } + + let mut derived_key = vec![0u8; KEY_LENGTH]; + let scrypt_params = ScryptParams::new(log_n, r, p); + scrypt(pass.as_bytes(), salt, &scrypt_params, &mut derived_key); + let derived_right_bits = &derived_key[0..KEY_LENGTH_AES]; + let derived_left_bits = &derived_key[KEY_LENGTH_AES..KEY_LENGTH]; + Ok((derived_right_bits.to_vec(), derived_left_bits.to_vec())) +} diff --git a/ethcore/evm/Cargo.toml b/ethcore/evm/Cargo.toml index 6efc05f4e2f0484ae7d7ee6f915e14a6d224d92d..fa7a99f9ded8f96897f0bb88d455a36751948834 100644 --- a/ethcore/evm/Cargo.toml +++ b/ethcore/evm/Cargo.toml @@ -5,8 +5,7 @@ authors = ["Parity Technologies "] [dependencies] bit-set = "0.4" -ethereum-types = "0.2" -evmjit = { path = "../../evmjit", optional = true } +ethereum-types = "0.3" heapsize = "0.4" lazy_static = "1.0" log = "0.3" @@ -19,6 +18,5 @@ memory-cache = { path = "../../util/memory_cache" } rustc-hex = "1.0" [features] -jit = ["evmjit"] evm-debug = [] evm-debug-tests = ["evm-debug"] diff --git a/ethcore/evm/src/benches/mod.rs b/ethcore/evm/src/benches/mod.rs index c87fda7bbfb538a980919733c5c9b0c7c9bee847..244c26985a846e14a20a7f4f40eba3827ff7827b 100644 --- a/ethcore/evm/src/benches/mod.rs +++ b/ethcore/evm/src/benches/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/evm/src/evm.rs b/ethcore/evm/src/evm.rs index 16dffe77c5381d9bf368b78916535c284532ea29..4c85b3702810552d61cf5fff9567bea470161cba 100644 --- a/ethcore/evm/src/evm.rs +++ b/ethcore/evm/src/evm.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/evm/src/factory.rs b/ethcore/evm/src/factory.rs index 140f1620a309fd7fb1b196dafa2d0b66af4c69ce..65a683cd4a8f1bda446f3f659dcc50f8de1aa7a0 100644 --- a/ethcore/evm/src/factory.rs +++ b/ethcore/evm/src/factory.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -32,23 +32,6 @@ pub struct Factory { impl Factory { /// Create fresh instance of VM /// Might choose implementation depending on supplied gas. - #[cfg(feature = "jit")] - pub fn create(&self, gas: &U256) -> Box { - match self.evm { - VMType::Jit => { - Box::new(super::jit::JitEvm::default()) - }, - VMType::Interpreter => if Self::can_fit_in_usize(gas) { - Box::new(super::interpreter::Interpreter::::new(self.evm_cache.clone())) - } else { - Box::new(super::interpreter::Interpreter::::new(self.evm_cache.clone())) - } - } - } - - /// Create fresh instance of VM - /// Might choose implementation depending on supplied gas. - #[cfg(not(feature = "jit"))] pub fn create(&self, gas: &U256) -> Box { match self.evm { VMType::Interpreter => if Self::can_fit_in_usize(gas) { @@ -74,17 +57,7 @@ impl Factory { } impl Default for Factory { - /// Returns jitvm factory - #[cfg(all(feature = "jit", not(test)))] - fn default() -> Factory { - Factory { - evm: VMType::Jit, - evm_cache: Arc::new(SharedCache::default()), - } - } - /// Returns native rust evm factory - #[cfg(any(not(feature = "jit"), test))] fn default() -> Factory { Factory { evm: VMType::Interpreter, @@ -101,24 +74,7 @@ fn test_create_vm() { /// Create tests by injecting different VM factories #[macro_export] macro_rules! evm_test( - (ignorejit => $name_test: ident: $name_jit: ident, $name_int: ident) => { - #[test] - #[ignore] - #[cfg(feature = "jit")] - fn $name_jit() { - $name_test(Factory::new(VMType::Jit, 1024 * 32)); - } - #[test] - fn $name_int() { - $name_test(Factory::new(VMType::Interpreter, 1024 * 32)); - } - }; - ($name_test: ident: $name_jit: ident, $name_int: ident) => { - #[test] - #[cfg(feature = "jit")] - fn $name_jit() { - $name_test(Factory::new(VMType::Jit, 1024 * 32)); - } + ($name_test: ident: $name_int: ident) => { #[test] fn $name_int() { $name_test(Factory::new(VMType::Interpreter, 1024 * 32)); @@ -129,14 +85,7 @@ macro_rules! evm_test( /// Create ignored tests by injecting different VM factories #[macro_export] macro_rules! evm_test_ignore( - ($name_test: ident: $name_jit: ident, $name_int: ident) => { - #[test] - #[ignore] - #[cfg(feature = "jit")] - #[cfg(feature = "ignored-tests")] - fn $name_jit() { - $name_test(Factory::new(VMType::Jit, 1024 * 32)); - } + ($name_test: ident: $name_int: ident) => { #[test] #[ignore] #[cfg(feature = "ignored-tests")] diff --git a/ethcore/evm/src/instructions.rs b/ethcore/evm/src/instructions.rs index 3fb9fb933962a48b2489c5684c19defc63cec6ff..76f99a9333861e0ba9354fbbe699087d416598ee 100644 --- a/ethcore/evm/src/instructions.rs +++ b/ethcore/evm/src/instructions.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -187,6 +187,9 @@ lazy_static! { arr[OR as usize] = InstructionInfo::new("OR", 2, 1, GasPriceTier::VeryLow); arr[XOR as usize] = InstructionInfo::new("XOR", 2, 1, GasPriceTier::VeryLow); arr[BYTE as usize] = InstructionInfo::new("BYTE", 2, 1, GasPriceTier::VeryLow); + arr[SHL as usize] = InstructionInfo::new("SHL", 2, 1, GasPriceTier::VeryLow); + arr[SHR as usize] = InstructionInfo::new("SHR", 2, 1, GasPriceTier::VeryLow); + arr[SAR as usize] = InstructionInfo::new("SAR", 2, 1, GasPriceTier::VeryLow); arr[ADDMOD as usize] = InstructionInfo::new("ADDMOD", 3, 1, GasPriceTier::Mid); arr[MULMOD as usize] = InstructionInfo::new("MULMOD", 3, 1, GasPriceTier::Mid); arr[SIGNEXTEND as usize] = InstructionInfo::new("SIGNEXTEND", 2, 1, GasPriceTier::Low); @@ -354,6 +357,12 @@ pub const XOR: Instruction = 0x18; pub const NOT: Instruction = 0x19; /// retrieve single byte from word pub const BYTE: Instruction = 0x1a; +/// shift left operation +pub const SHL: Instruction = 0x1b; +/// logical shift right operation +pub const SHR: Instruction = 0x1c; +/// arithmetic shift right operation +pub const SAR: Instruction = 0x1d; /// compute SHA3-256 hash pub const SHA3: Instruction = 0x20; @@ -589,4 +598,3 @@ pub const REVERT: Instruction = 0xfd; pub const STATICCALL: Instruction = 0xfa; /// halt execution and register account for later deletion pub const SUICIDE: Instruction = 0xff; - diff --git a/ethcore/evm/src/interpreter/gasometer.rs b/ethcore/evm/src/interpreter/gasometer.rs index beb22447fe4c08257d27868c040e93d8d2d87db9..85ea8ee487e73ac48462954ef6c8e5cec4cd8ac3 100644 --- a/ethcore/evm/src/interpreter/gasometer.rs +++ b/ethcore/evm/src/interpreter/gasometer.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -316,7 +316,6 @@ impl Gasometer { } } - #[inline] fn mem_needed_const(mem: &U256, add: usize) -> vm::Result { Gas::from_u256(overflowing!(mem.overflowing_add(U256::from(add)))) @@ -369,4 +368,3 @@ fn test_calculate_mem_cost() { assert_eq!(new_mem_gas, 3); assert_eq!(mem_size, 32); } - diff --git a/ethcore/evm/src/interpreter/informant.rs b/ethcore/evm/src/interpreter/informant.rs index f07d11ff7aa5f3a86f5020eff29a0bc624efb5de..ca04be844f43f196228a993036c96d6047e00882 100644 --- a/ethcore/evm/src/interpreter/informant.rs +++ b/ethcore/evm/src/interpreter/informant.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/evm/src/interpreter/memory.rs b/ethcore/evm/src/interpreter/memory.rs index f646d01985d83dce2f1dc95e1b25c1e729ce1844..843aeef3b5787ae3645209c5807c1dba74dbc151 100644 --- a/ethcore/evm/src/interpreter/memory.rs +++ b/ethcore/evm/src/interpreter/memory.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/evm/src/interpreter/mod.rs b/ethcore/evm/src/interpreter/mod.rs index 05d1fb78853fc0dfacd5bf421dd0d37c78aae8d9..ef9b3fb9734640b959c57b831847fef723c53110 100644 --- a/ethcore/evm/src/interpreter/mod.rs +++ b/ethcore/evm/src/interpreter/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -103,7 +103,6 @@ enum InstructionResult { StopExecution, } - /// Intepreter EVM implementation pub struct Interpreter { mem: Vec, @@ -224,7 +223,8 @@ impl Interpreter { (instruction == instructions::CREATE2 && !schedule.have_create2) || (instruction == instructions::STATICCALL && !schedule.have_static_call) || ((instruction == instructions::RETURNDATACOPY || instruction == instructions::RETURNDATASIZE) && !schedule.have_return_data) || - (instruction == instructions::REVERT && !schedule.have_revert) { + (instruction == instructions::REVERT && !schedule.have_revert) || + ((instruction == instructions::SHL || instruction == instructions::SHR || instruction == instructions::SAR) && !schedule.have_bitwise_shifting) { return Err(vm::Error::BadInstruction { instruction: instruction @@ -871,6 +871,58 @@ impl Interpreter { }); } }, + instructions::SHL => { + const CONST_256: U256 = U256([256, 0, 0, 0]); + + let shift = stack.pop_back(); + let value = stack.pop_back(); + + let result = if shift >= CONST_256 { + U256::zero() + } else { + value << (shift.as_u32() as usize) + }; + stack.push(result); + }, + instructions::SHR => { + const CONST_256: U256 = U256([256, 0, 0, 0]); + + let shift = stack.pop_back(); + let value = stack.pop_back(); + + let result = if shift >= CONST_256 { + U256::zero() + } else { + value >> (shift.as_u32() as usize) + }; + stack.push(result); + }, + instructions::SAR => { + // We cannot use get_and_reset_sign/set_sign here, because the rounding looks different. + + const CONST_256: U256 = U256([256, 0, 0, 0]); + const CONST_HIBIT: U256 = U256([0, 0, 0, 0x8000000000000000]); + + let shift = stack.pop_back(); + let value = stack.pop_back(); + let sign = value & CONST_HIBIT != U256::zero(); + + let result = if shift >= CONST_256 { + if sign { + U256::max_value() + } else { + U256::zero() + } + } else { + let shift = shift.as_u32() as usize; + let mut shifted = value >> shift; + if sign { + shifted = shifted | (U256::max_value() << (256 - shift)); + } + shifted + }; + stack.push(result); + }, _ => { return Err(vm::Error::BadInstruction { instruction: instruction @@ -906,7 +958,6 @@ fn address_to_u256(value: Address) -> U256 { U256::from(&*H256::from(value)) } - #[cfg(test)] mod tests { use std::sync::Arc; diff --git a/ethcore/evm/src/interpreter/shared_cache.rs b/ethcore/evm/src/interpreter/shared_cache.rs index 30bc5b677d5243a9f6d593f98923e96b680e752f..d4e992c90e8e90c6e066fd25e81662a3ca82d2e3 100644 --- a/ethcore/evm/src/interpreter/shared_cache.rs +++ b/ethcore/evm/src/interpreter/shared_cache.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -91,7 +91,6 @@ impl Default for SharedCache { } } - #[test] fn test_find_jump_destinations() { use rustc_hex::FromHex; diff --git a/ethcore/evm/src/interpreter/stack.rs b/ethcore/evm/src/interpreter/stack.rs index cbe40fb67fbaec88c1176de2f8a4965b7e962ee4..3902b8ff7678fae888e9b0a926635c8372319e3d 100644 --- a/ethcore/evm/src/interpreter/stack.rs +++ b/ethcore/evm/src/interpreter/stack.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -95,4 +95,3 @@ impl Stack for VecStack { &self.stack[self.stack.len() - no_from_top .. self.stack.len()] } } - diff --git a/ethcore/evm/src/jit.rs b/ethcore/evm/src/jit.rs deleted file mode 100644 index d94bb7b8e084a94980041ef8420036cdb87a4fe1..0000000000000000000000000000000000000000 --- a/ethcore/evm/src/jit.rs +++ /dev/null @@ -1,414 +0,0 @@ -// Copyright 2015-2017 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 . - -//! Just in time compiler execution environment. -use bigint::prelude::U256; -use bigint::hash::H256; -use util::*; -use evmjit; -use evm::{self, GasLeft}; -use evm::CallType; -use vm::{self, Vm}; - -/// Should be used to convert jit types to ethcore -trait FromJit: Sized { - fn from_jit(input: T) -> Self; -} - -/// Should be used to covert ethcore types to jit -trait IntoJit { - fn into_jit(self) -> T; -} - -impl<'a> FromJit<&'a evmjit::I256> for U256 { - fn from_jit(input: &'a evmjit::I256) -> Self { - unsafe { - let mut res: U256 = mem::uninitialized(); - ptr::copy(input.words.as_ptr(), res.0.as_mut_ptr(), 4); - res - } - } -} - -impl<'a> FromJit<&'a evmjit::I256> for H256 { - fn from_jit(input: &'a evmjit::I256) -> Self { - let u = U256::from_jit(input); - H256::from(&u) - } -} - -impl<'a> FromJit<&'a evmjit::I256> for Address { - fn from_jit(input: &'a evmjit::I256) -> Self { - Address::from(H256::from_jit(input)) - } -} - -impl<'a> FromJit<&'a evmjit::H256> for H256 { - fn from_jit(input: &'a evmjit::H256) -> Self { - H256::from_jit(&evmjit::I256::from(input.clone())) - } -} - -impl<'a> FromJit<&'a evmjit::H256> for Address { - fn from_jit(input: &'a evmjit::H256) -> Self { - Address::from(H256::from_jit(input)) - } -} - -impl IntoJit for U256 { - fn into_jit(self) -> evmjit::I256 { - unsafe { - let mut res: evmjit::I256 = mem::uninitialized(); - ptr::copy(self.0.as_ptr(), res.words.as_mut_ptr(), 4); - res - } - } -} - -impl IntoJit for H256 { - fn into_jit(self) -> evmjit::I256 { - let mut ret = [0; 4]; - let len = self.len(); - for i in 0..len { - let rev = len - 1 - i; - let pos = rev / 8; - ret[pos] += (self[i] as u64) << ((rev % 8) * 8); - } - evmjit::I256 { words: ret } - } -} - -impl IntoJit for H256 { - fn into_jit(self) -> evmjit::H256 { - let i: evmjit::I256 = self.into_jit(); - From::from(i) - } -} - -impl IntoJit for Address { - fn into_jit(self) -> evmjit::I256 { - H256::from(self).into_jit() - } -} - -impl IntoJit for Address { - fn into_jit(self) -> evmjit::H256 { - H256::from(self).into_jit() - } -} - -/// Externalities adapter. Maps callbacks from evmjit to externalities trait. -/// -/// Evmjit doesn't have to know about children execution failures. -/// This adapter 'catches' them and moves upstream. -struct ExtAdapter<'a> { - ext: &'a mut evm::Ext, - address: Address -} - -impl<'a> ExtAdapter<'a> { - fn new(ext: &'a mut evm::Ext, address: Address) -> Self { - ExtAdapter { - ext: ext, - address: address - } - } -} - -impl<'a> evmjit::Ext for ExtAdapter<'a> { - fn sload(&self, key: *const evmjit::I256, out_value: *mut evmjit::I256) { - unsafe { - let i = H256::from_jit(&*key); - let o = self.ext.storage_at(&i); - *out_value = o.into_jit(); - } - } - - fn sstore(&mut self, key: *const evmjit::I256, value: *const evmjit::I256) { - let key = unsafe { H256::from_jit(&*key) }; - let value = unsafe { H256::from_jit(&*value) }; - let old_value = self.ext.storage_at(&key); - // if SSTORE nonzero -> zero, increment refund count - if !old_value.is_zero() && value.is_zero() { - self.ext.inc_sstore_clears(); - } - self.ext.set_storage(key, value); - } - - fn balance(&self, address: *const evmjit::H256, out_value: *mut evmjit::I256) { - unsafe { - let a = Address::from_jit(&*address); - let o = self.ext.balance(&a); - *out_value = o.into_jit(); - } - } - - fn blockhash(&self, number: *const evmjit::I256, out_hash: *mut evmjit::H256) { - unsafe { - let n = U256::from_jit(&*number); - let o = self.ext.blockhash(&n); - *out_hash = o.into_jit(); - } - } - - fn create(&mut self, - io_gas: *mut u64, - value: *const evmjit::I256, - init_beg: *const u8, - init_size: u64, - address: *mut evmjit::H256) { - - let gas = unsafe { U256::from(*io_gas) }; - let value = unsafe { U256::from_jit(&*value) }; - let code = unsafe { slice::from_raw_parts(init_beg, init_size as usize) }; - - // check if balance is sufficient and we are not too deep - if self.ext.balance(&self.address) >= value && self.ext.depth() < self.ext.schedule().max_depth { - match self.ext.create(&gas, &value, code) { - evm::ContractCreateResult::Created(new_address, gas_left) => unsafe { - *address = new_address.into_jit(); - *io_gas = gas_left.low_u64(); - }, - evm::ContractCreateResult::Failed => unsafe { - *address = Address::new().into_jit(); - *io_gas = 0; - } - } - } else { - unsafe { *address = Address::new().into_jit(); } - } - } - - fn call(&mut self, - io_gas: *mut u64, - call_gas: u64, - sender_address: *const evmjit::H256, - receive_address: *const evmjit::H256, - code_address: *const evmjit::H256, - transfer_value: *const evmjit::I256, - // We are ignoring apparent value - it's handled in externalities. - _apparent_value: *const evmjit::I256, - in_beg: *const u8, - in_size: u64, - out_beg: *mut u8, - out_size: u64) -> bool { - - let mut gas = unsafe { U256::from(*io_gas) }; - let mut call_gas = U256::from(call_gas); - let mut gas_cost = call_gas; - let sender_address = unsafe { Address::from_jit(&*sender_address) }; - let receive_address = unsafe { Address::from_jit(&*receive_address) }; - let code_address = unsafe { Address::from_jit(&*code_address) }; - let transfer_value = unsafe { U256::from_jit(&*transfer_value) }; - - // receive address and code address are the same in normal calls - let is_callcode = receive_address != code_address; - let is_delegatecall = is_callcode && sender_address != receive_address; - - let value = if is_delegatecall { None } else { Some(transfer_value) }; - - if !is_callcode && !self.ext.exists(&code_address) { - gas_cost = gas_cost + U256::from(self.ext.schedule().call_new_account_gas); - } - - if transfer_value > U256::zero() { - assert!(self.ext.schedule().call_value_transfer_gas > self.ext.schedule().call_stipend, "overflow possible"); - gas_cost = gas_cost + U256::from(self.ext.schedule().call_value_transfer_gas); - call_gas = call_gas + U256::from(self.ext.schedule().call_stipend); - } - - if gas_cost > gas { - unsafe { - *io_gas = -1i64 as u64; - return false; - } - } - - gas = gas - gas_cost; - - // check if balance is sufficient and we are not too deep - if self.ext.balance(&self.address) < transfer_value || self.ext.depth() >= self.ext.schedule().max_depth { - unsafe { - *io_gas = (gas + call_gas).low_u64(); - return false; - } - } - - let call_type = match (is_callcode, is_delegatecall) { - (_, true) => CallType::DelegateCall, - (true, false) => CallType::CallCode, - (false, false) => CallType::Call, - }; - - match self.ext.call( - &call_gas, - &sender_address, - &receive_address, - value, - unsafe { slice::from_raw_parts(in_beg, in_size as usize) }, - &code_address, - unsafe { slice::from_raw_parts_mut(out_beg, out_size as usize) }, - call_type, - ) { - evm::MessageCallResult::Success(gas_left) => unsafe { - *io_gas = (gas + gas_left).low_u64(); - true - }, - evm::MessageCallResult::Failed => unsafe { - *io_gas = gas.low_u64(); - false - } - } - } - - fn log(&mut self, - beg: *const u8, - size: u64, - topic1: *const evmjit::H256, - topic2: *const evmjit::H256, - topic3: *const evmjit::H256, - topic4: *const evmjit::H256) { - - unsafe { - let mut topics = vec![]; - if !topic1.is_null() { - topics.push(H256::from_jit(&*topic1)); - } - - if !topic2.is_null() { - topics.push(H256::from_jit(&*topic2)); - } - - if !topic3.is_null() { - topics.push(H256::from_jit(&*topic3)); - } - - if !topic4.is_null() { - topics.push(H256::from_jit(&*topic4)); - } - - let bytes_ref: &[u8] = slice::from_raw_parts(beg, size as usize); - self.ext.log(topics, bytes_ref); - } - } - - fn extcode(&self, address: *const evmjit::H256, size: *mut u64) -> *const u8 { - unsafe { - let code = self.ext.extcode(&Address::from_jit(&*address)); - *size = code.len() as u64; - let ptr = code.as_ptr(); - mem::forget(code); - ptr - } - } -} - -#[derive(Default)] -pub struct JitEvm { - context: Option, -} - -impl vm::Vm for JitEvm { - fn exec(&mut self, params: ActionParams, ext: &mut evm::Ext) -> evm::Result { - // Dirty hack. This is unsafe, but we interact with ffi, so it's justified. - let ext_adapter: ExtAdapter<'static> = unsafe { ::std::mem::transmute(ExtAdapter::new(ext, params.address.clone())) }; - let mut ext_handle = evmjit::ExtHandle::new(ext_adapter); - assert!(params.gas <= U256::from(i64::max_value() as u64), "evmjit max gas is 2 ^ 63"); - assert!(params.gas_price <= U256::from(i64::max_value() as u64), "evmjit max gas is 2 ^ 63"); - - let call_data = params.data.unwrap_or_else(Vec::new); - let code = params.code.unwrap_or_else(Vec::new); - - let mut data = evmjit::RuntimeDataHandle::new(); - data.gas = params.gas.low_u64() as i64; - data.gas_price = params.gas_price.low_u64() as i64; - data.call_data = call_data.as_ptr(); - data.call_data_size = call_data.len() as u64; - mem::forget(call_data); - data.code = code.as_ptr(); - data.code_size = code.len() as u64; - data.code_hash = code.sha3().into_jit(); - mem::forget(code); - data.address = params.address.into_jit(); - data.caller = params.sender.into_jit(); - data.origin = params.origin.into_jit(); - data.transfer_value = match params.value { - ActionValue::Transfer(val) => val.into_jit(), - ActionValue::Apparent(val) => val.into_jit() - }; - data.apparent_value = data.transfer_value; - - let mut schedule = evmjit::ScheduleHandle::new(); - schedule.have_delegate_call = ext.schedule().have_delegate_call; - - data.author = ext.env_info().author.clone().into_jit(); - data.difficulty = ext.env_info().difficulty.into_jit(); - data.gas_limit = ext.env_info().gas_limit.into_jit(); - data.number = ext.env_info().number; - // don't really know why jit timestamp is int.. - data.timestamp = ext.env_info().timestamp as i64; - - self.context = Some(unsafe { evmjit::ContextHandle::new(data, schedule, &mut ext_handle) }); - let mut context = self.context.as_mut().expect("context handle set on the prior line; qed"); - let res = context.exec(); - - match res { - evmjit::ReturnCode::Stop => Ok(GasLeft::Known(U256::from(context.gas_left()))), - evmjit::ReturnCode::Return => - Ok(GasLeft::NeedsReturn(U256::from(context.gas_left()), context.output_data())), - evmjit::ReturnCode::Suicide => { - ext.suicide(&Address::from_jit(&context.suicide_refund_address())); - Ok(GasLeft::Known(U256::from(context.gas_left()))) - }, - evmjit::ReturnCode::OutOfGas => Err(vm::Error::OutOfGas), - _err => Err(vm::Error::Internal) - } - } -} - -#[test] -fn test_to_and_from_u256() { - let u = U256::from_str("d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3").unwrap(); - let j = u.into_jit(); - let u2 = U256::from_jit(&j); - assert_eq!(u, u2); -} - -#[test] -fn test_to_and_from_h256() { - let h = H256::from_str("d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3").unwrap(); - let j: ::evmjit::I256 = h.clone().into_jit(); - let h2 = H256::from_jit(&j); - - assert_eq!(h, h2); - - let j: ::evmjit::H256 = h.clone().into_jit(); - let h2 = H256::from_jit(&j); - assert_eq!(h, h2); -} - -#[test] -fn test_to_and_from_address() { - let a = Address::from_str("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba").unwrap(); - let j: ::evmjit::I256 = a.clone().into_jit(); - let a2 = Address::from_jit(&j); - - assert_eq!(a, a2); - - let j: ::evmjit::H256 = a.clone().into_jit(); - let a2 = Address::from_jit(&j); - assert_eq!(a, a2); -} diff --git a/ethcore/evm/src/lib.rs b/ethcore/evm/src/lib.rs index b61c8aa2a9dc41c0a8202e1b1bd8c4d982187a43..6eca25f42f5c5f46bff0b9fb71efc7c40c026c84 100644 --- a/ethcore/evm/src/lib.rs +++ b/ethcore/evm/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -30,9 +30,6 @@ extern crate lazy_static; #[cfg_attr(feature = "evm-debug", macro_use)] extern crate log; -#[cfg(feature = "jit")] -extern crate evmjit; - #[cfg(test)] extern crate rustc_hex; @@ -44,12 +41,9 @@ pub mod factory; mod vmtype; mod instructions; -#[cfg(feature = "jit" )] -mod jit; - #[cfg(test)] mod tests; -#[cfg(all(feature="benches", test))] +#[cfg(all(feature = "benches", test))] mod benches; pub use vm::{ diff --git a/ethcore/evm/src/tests.rs b/ethcore/evm/src/tests.rs index a219ae988076f0040ef3b3dd79cd88c1a6f67f2f..b62faf87d773b3e7b5525e6b63be2ab05c26c8b8 100644 --- a/ethcore/evm/src/tests.rs +++ b/ethcore/evm/src/tests.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -26,9 +26,9 @@ use vm::tests::{FakeExt, FakeCall, FakeCallType, test_finalize}; use factory::Factory; use vmtype::VMType; -evm_test!{test_add: test_add_jit, test_add_int} +evm_test!{test_add: test_add_int} fn test_add(factory: super::Factory) { - let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); + let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); let code = "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01600055".from_hex().unwrap(); let mut params = ActionParams::default(); @@ -46,7 +46,7 @@ fn test_add(factory: super::Factory) { assert_store(&ext, 0, "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"); } -evm_test!{test_sha3: test_sha3_jit, test_sha3_int} +evm_test!{test_sha3: test_sha3_int} fn test_sha3(factory: super::Factory) { let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); let code = "6000600020600055".from_hex().unwrap(); @@ -66,7 +66,7 @@ fn test_sha3(factory: super::Factory) { assert_store(&ext, 0, "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"); } -evm_test!{test_address: test_address_jit, test_address_int} +evm_test!{test_address: test_address_int} fn test_address(factory: super::Factory) { let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); let code = "30600055".from_hex().unwrap(); @@ -86,7 +86,7 @@ fn test_address(factory: super::Factory) { assert_store(&ext, 0, "0000000000000000000000000f572e5295c57f15886f9b263e2f6d2d6c7b5ec6"); } -evm_test!{test_origin: test_origin_jit, test_origin_int} +evm_test!{test_origin: test_origin_int} fn test_origin(factory: super::Factory) { let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); let origin = Address::from_str("cd1722f2947def4cf144679da39c4c32bdc35681").unwrap(); @@ -108,7 +108,7 @@ fn test_origin(factory: super::Factory) { assert_store(&ext, 0, "000000000000000000000000cd1722f2947def4cf144679da39c4c32bdc35681"); } -evm_test!{test_sender: test_sender_jit, test_sender_int} +evm_test!{test_sender: test_sender_int} fn test_sender(factory: super::Factory) { let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); let sender = Address::from_str("cd1722f2947def4cf144679da39c4c32bdc35681").unwrap(); @@ -130,7 +130,7 @@ fn test_sender(factory: super::Factory) { assert_store(&ext, 0, "000000000000000000000000cd1722f2947def4cf144679da39c4c32bdc35681"); } -evm_test!{test_extcodecopy: test_extcodecopy_jit, test_extcodecopy_int} +evm_test!{test_extcodecopy: test_extcodecopy_int} fn test_extcodecopy(factory: super::Factory) { // 33 - sender // 3b - extcodesize @@ -165,7 +165,7 @@ fn test_extcodecopy(factory: super::Factory) { assert_store(&ext, 0, "6005600055000000000000000000000000000000000000000000000000000000"); } -evm_test!{test_log_empty: test_log_empty_jit, test_log_empty_int} +evm_test!{test_log_empty: test_log_empty_int} fn test_log_empty(factory: super::Factory) { let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); let code = "60006000a0".from_hex().unwrap(); @@ -187,7 +187,7 @@ fn test_log_empty(factory: super::Factory) { assert!(ext.logs[0].data.is_empty()); } -evm_test!{test_log_sender: test_log_sender_jit, test_log_sender_int} +evm_test!{test_log_sender: test_log_sender_int} fn test_log_sender(factory: super::Factory) { // 60 ff - push ff // 60 00 - push 00 @@ -220,7 +220,7 @@ fn test_log_sender(factory: super::Factory) { assert_eq!(ext.logs[0].data, "ff00000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap()); } -evm_test!{test_blockhash: test_blockhash_jit, test_blockhash_int} +evm_test!{test_blockhash: test_blockhash_int} fn test_blockhash(factory: super::Factory) { let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); let code = "600040600055".from_hex().unwrap(); @@ -242,7 +242,7 @@ fn test_blockhash(factory: super::Factory) { assert_eq!(ext.store.get(&H256::new()).unwrap(), &blockhash); } -evm_test!{test_calldataload: test_calldataload_jit, test_calldataload_int} +evm_test!{test_calldataload: test_calldataload_int} fn test_calldataload(factory: super::Factory) { let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); let code = "600135600055".from_hex().unwrap(); @@ -265,7 +265,7 @@ fn test_calldataload(factory: super::Factory) { } -evm_test!{test_author: test_author_jit, test_author_int} +evm_test!{test_author: test_author_int} fn test_author(factory: super::Factory) { let author = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); let code = "41600055".from_hex().unwrap(); @@ -285,7 +285,7 @@ fn test_author(factory: super::Factory) { assert_store(&ext, 0, "0000000000000000000000000f572e5295c57f15886f9b263e2f6d2d6c7b5ec6"); } -evm_test!{test_timestamp: test_timestamp_jit, test_timestamp_int} +evm_test!{test_timestamp: test_timestamp_int} fn test_timestamp(factory: super::Factory) { let timestamp = 0x1234; let code = "42600055".from_hex().unwrap(); @@ -305,7 +305,7 @@ fn test_timestamp(factory: super::Factory) { assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000001234"); } -evm_test!{test_number: test_number_jit, test_number_int} +evm_test!{test_number: test_number_int} fn test_number(factory: super::Factory) { let number = 0x1234; let code = "43600055".from_hex().unwrap(); @@ -325,7 +325,7 @@ fn test_number(factory: super::Factory) { assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000001234"); } -evm_test!{test_difficulty: test_difficulty_jit, test_difficulty_int} +evm_test!{test_difficulty: test_difficulty_int} fn test_difficulty(factory: super::Factory) { let difficulty = U256::from(0x1234); let code = "44600055".from_hex().unwrap(); @@ -345,7 +345,7 @@ fn test_difficulty(factory: super::Factory) { assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000001234"); } -evm_test!{test_gas_limit: test_gas_limit_jit, test_gas_limit_int} +evm_test!{test_gas_limit: test_gas_limit_int} fn test_gas_limit(factory: super::Factory) { let gas_limit = U256::from(0x1234); let code = "45600055".from_hex().unwrap(); @@ -365,7 +365,7 @@ fn test_gas_limit(factory: super::Factory) { assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000001234"); } -evm_test!{test_mul: test_mul_jit, test_mul_int} +evm_test!{test_mul: test_mul_int} fn test_mul(factory: super::Factory) { let code = "65012365124623626543219002600055".from_hex().unwrap(); @@ -383,7 +383,7 @@ fn test_mul(factory: super::Factory) { assert_eq!(gas_left, U256::from(79_983)); } -evm_test!{test_sub: test_sub_jit, test_sub_int} +evm_test!{test_sub: test_sub_int} fn test_sub(factory: super::Factory) { let code = "65012365124623626543219003600055".from_hex().unwrap(); @@ -401,7 +401,7 @@ fn test_sub(factory: super::Factory) { assert_eq!(gas_left, U256::from(79_985)); } -evm_test!{test_div: test_div_jit, test_div_int} +evm_test!{test_div: test_div_int} fn test_div(factory: super::Factory) { let code = "65012365124623626543219004600055".from_hex().unwrap(); @@ -419,7 +419,7 @@ fn test_div(factory: super::Factory) { assert_eq!(gas_left, U256::from(79_983)); } -evm_test!{test_div_zero: test_div_zero_jit, test_div_zero_int} +evm_test!{test_div_zero: test_div_zero_int} fn test_div_zero(factory: super::Factory) { let code = "6501236512462360009004600055".from_hex().unwrap(); @@ -437,7 +437,7 @@ fn test_div_zero(factory: super::Factory) { assert_eq!(gas_left, U256::from(94_983)); } -evm_test!{test_mod: test_mod_jit, test_mod_int} +evm_test!{test_mod: test_mod_int} fn test_mod(factory: super::Factory) { let code = "650123651246236265432290066000556501236512462360009006600155".from_hex().unwrap(); @@ -456,7 +456,7 @@ fn test_mod(factory: super::Factory) { assert_eq!(gas_left, U256::from(74_966)); } -evm_test!{test_smod: test_smod_jit, test_smod_int} +evm_test!{test_smod: test_smod_int} fn test_smod(factory: super::Factory) { let code = "650123651246236265432290076000556501236512462360009007600155".from_hex().unwrap(); @@ -475,7 +475,7 @@ fn test_smod(factory: super::Factory) { assert_eq!(gas_left, U256::from(74_966)); } -evm_test!{test_sdiv: test_sdiv_jit, test_sdiv_int} +evm_test!{test_sdiv: test_sdiv_int} fn test_sdiv(factory: super::Factory) { let code = "650123651246236265432290056000556501236512462360009005600155".from_hex().unwrap(); @@ -494,7 +494,7 @@ fn test_sdiv(factory: super::Factory) { assert_eq!(gas_left, U256::from(74_966)); } -evm_test!{test_exp: test_exp_jit, test_exp_int} +evm_test!{test_exp: test_exp_int} fn test_exp(factory: super::Factory) { let code = "6016650123651246230a6000556001650123651246230a6001556000650123651246230a600255".from_hex().unwrap(); @@ -514,7 +514,7 @@ fn test_exp(factory: super::Factory) { assert_eq!(gas_left, U256::from(39_923)); } -evm_test!{test_comparison: test_comparison_jit, test_comparison_int} +evm_test!{test_comparison: test_comparison_int} fn test_comparison(factory: super::Factory) { let code = "601665012365124623818181811060005511600155146002556415235412358014600355".from_hex().unwrap(); @@ -535,7 +535,7 @@ fn test_comparison(factory: super::Factory) { assert_eq!(gas_left, U256::from(49_952)); } -evm_test!{test_signed_comparison: test_signed_comparison_jit, test_signed_comparison_int} +evm_test!{test_signed_comparison: test_signed_comparison_int} fn test_signed_comparison(factory: super::Factory) { let code = "60106000036010818112600055136001556010601060000381811260025513600355".from_hex().unwrap(); @@ -556,7 +556,7 @@ fn test_signed_comparison(factory: super::Factory) { assert_eq!(gas_left, U256::from(49_940)); } -evm_test!{test_bitops: test_bitops_jit, test_bitops_int} +evm_test!{test_bitops: test_bitops_int} fn test_bitops(factory: super::Factory) { let code = "60ff610ff08181818116600055176001551860025560008015600355198015600455600555".from_hex().unwrap(); @@ -579,7 +579,7 @@ fn test_bitops(factory: super::Factory) { assert_eq!(gas_left, U256::from(44_937)); } -evm_test!{test_addmod_mulmod: test_addmod_mulmod_jit, test_addmod_mulmod_int} +evm_test!{test_addmod_mulmod: test_addmod_mulmod_int} fn test_addmod_mulmod(factory: super::Factory) { let code = "60ff60f060108282820860005509600155600060f0601082828208196002550919600355".from_hex().unwrap(); @@ -600,7 +600,7 @@ fn test_addmod_mulmod(factory: super::Factory) { assert_eq!(gas_left, U256::from(19_914)); } -evm_test!{test_byte: test_byte_jit, test_byte_int} +evm_test!{test_byte: test_byte_int} fn test_byte(factory: super::Factory) { let code = "60f061ffff1a600055610fff601f1a600155".from_hex().unwrap(); @@ -619,7 +619,7 @@ fn test_byte(factory: super::Factory) { assert_eq!(gas_left, U256::from(74_976)); } -evm_test!{test_signextend: test_signextend_jit, test_signextend_int} +evm_test!{test_signextend: test_signextend_int} fn test_signextend(factory: super::Factory) { let code = "610fff60020b60005560ff60200b600155".from_hex().unwrap(); @@ -659,7 +659,7 @@ fn test_badinstruction_int() { } } -evm_test!{test_pop: test_pop_jit, test_pop_int} +evm_test!{test_pop: test_pop_int} fn test_pop(factory: super::Factory) { let code = "60f060aa50600055".from_hex().unwrap(); @@ -677,7 +677,7 @@ fn test_pop(factory: super::Factory) { assert_eq!(gas_left, U256::from(79_989)); } -evm_test!{test_extops: test_extops_jit, test_extops_int} +evm_test!{test_extops: test_extops_int} fn test_extops(factory: super::Factory) { let code = "5a6001555836553a600255386003553460045560016001526016590454600555".from_hex().unwrap(); @@ -702,7 +702,7 @@ fn test_extops(factory: super::Factory) { assert_eq!(gas_left, U256::from(29_898)); } -evm_test!{test_jumps: test_jumps_jit, test_jumps_int} +evm_test!{test_jumps: test_jumps_int} fn test_jumps(factory: super::Factory) { let code = "600160015560066000555b60016000540380806000551560245760015402600155600a565b".from_hex().unwrap(); @@ -722,7 +722,7 @@ fn test_jumps(factory: super::Factory) { assert_eq!(gas_left, U256::from(54_117)); } -evm_test!{test_calls: test_calls_jit, test_calls_int} +evm_test!{test_calls: test_calls_int} fn test_calls(factory: super::Factory) { let code = "600054602d57600160005560006000600060006050610998610100f160006000600060006050610998610100f25b".from_hex().unwrap(); @@ -766,7 +766,7 @@ fn test_calls(factory: super::Factory) { assert_eq!(ext.calls.len(), 2); } -evm_test!{test_create_in_staticcall: test_create_in_staticcall_jit, test_create_in_staticcall_int} +evm_test!{test_create_in_staticcall: test_create_in_staticcall_int} fn test_create_in_staticcall(factory: super::Factory) { let code = "600060006064f000".from_hex().unwrap(); @@ -787,6 +787,273 @@ fn test_create_in_staticcall(factory: super::Factory) { assert_eq!(ext.calls.len(), 0); } +evm_test!{test_shl: test_shl_int} +fn test_shl(factory: super::Factory) { + push_two_pop_one_constantinople_test( + &factory, + 0x1b, + "0000000000000000000000000000000000000000000000000000000000000001", + "00", + "0000000000000000000000000000000000000000000000000000000000000001"); + push_two_pop_one_constantinople_test( + &factory, + 0x1b, + "0000000000000000000000000000000000000000000000000000000000000001", + "01", + "0000000000000000000000000000000000000000000000000000000000000002"); + push_two_pop_one_constantinople_test( + &factory, + 0x1b, + "0000000000000000000000000000000000000000000000000000000000000001", + "ff", + "8000000000000000000000000000000000000000000000000000000000000000"); + push_two_pop_one_constantinople_test( + &factory, + 0x1b, + "0000000000000000000000000000000000000000000000000000000000000001", + "0100", + "0000000000000000000000000000000000000000000000000000000000000000"); + push_two_pop_one_constantinople_test( + &factory, + 0x1b, + "0000000000000000000000000000000000000000000000000000000000000001", + "0101", + "0000000000000000000000000000000000000000000000000000000000000000"); + push_two_pop_one_constantinople_test( + &factory, + 0x1b, + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "00", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); + push_two_pop_one_constantinople_test( + &factory, + 0x1b, + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "01", + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"); + push_two_pop_one_constantinople_test( + &factory, + 0x1b, + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ff", + "8000000000000000000000000000000000000000000000000000000000000000"); + push_two_pop_one_constantinople_test( + &factory, + 0x1b, + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "0100", + "0000000000000000000000000000000000000000000000000000000000000000"); + push_two_pop_one_constantinople_test( + &factory, + 0x1b, + "0000000000000000000000000000000000000000000000000000000000000000", + "01", + "0000000000000000000000000000000000000000000000000000000000000000"); + push_two_pop_one_constantinople_test( + &factory, + 0x1b, + "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "01", + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"); +} + +evm_test!{test_shr: test_shr_int} +fn test_shr(factory: super::Factory) { + push_two_pop_one_constantinople_test( + &factory, + 0x1c, + "0000000000000000000000000000000000000000000000000000000000000001", + "00", + "0000000000000000000000000000000000000000000000000000000000000001"); + push_two_pop_one_constantinople_test( + &factory, + 0x1c, + "0000000000000000000000000000000000000000000000000000000000000001", + "01", + "0000000000000000000000000000000000000000000000000000000000000000"); + push_two_pop_one_constantinople_test( + &factory, + 0x1c, + "8000000000000000000000000000000000000000000000000000000000000000", + "01", + "4000000000000000000000000000000000000000000000000000000000000000"); + push_two_pop_one_constantinople_test( + &factory, + 0x1c, + "8000000000000000000000000000000000000000000000000000000000000000", + "ff", + "0000000000000000000000000000000000000000000000000000000000000001"); + push_two_pop_one_constantinople_test( + &factory, + 0x1c, + "8000000000000000000000000000000000000000000000000000000000000000", + "0100", + "0000000000000000000000000000000000000000000000000000000000000000"); + push_two_pop_one_constantinople_test( + &factory, + 0x1c, + "8000000000000000000000000000000000000000000000000000000000000000", + "0101", + "0000000000000000000000000000000000000000000000000000000000000000"); + push_two_pop_one_constantinople_test( + &factory, + 0x1c, + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "00", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); + push_two_pop_one_constantinople_test( + &factory, + 0x1c, + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "01", + "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); + push_two_pop_one_constantinople_test( + &factory, + 0x1c, + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ff", + "0000000000000000000000000000000000000000000000000000000000000001"); + push_two_pop_one_constantinople_test( + &factory, + 0x1c, + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "0100", + "0000000000000000000000000000000000000000000000000000000000000000"); + push_two_pop_one_constantinople_test( + &factory, + 0x1c, + "0000000000000000000000000000000000000000000000000000000000000000", + "01", + "0000000000000000000000000000000000000000000000000000000000000000"); +} + +evm_test!{test_sar: test_sar_int} +fn test_sar(factory: super::Factory) { + push_two_pop_one_constantinople_test( + &factory, + 0x1d, + "0000000000000000000000000000000000000000000000000000000000000001", + "00", + "0000000000000000000000000000000000000000000000000000000000000001"); + push_two_pop_one_constantinople_test( + &factory, + 0x1d, + "0000000000000000000000000000000000000000000000000000000000000001", + "01", + "0000000000000000000000000000000000000000000000000000000000000000"); + push_two_pop_one_constantinople_test( + &factory, + 0x1d, + "8000000000000000000000000000000000000000000000000000000000000000", + "01", + "c000000000000000000000000000000000000000000000000000000000000000"); + push_two_pop_one_constantinople_test( + &factory, + 0x1d, + "8000000000000000000000000000000000000000000000000000000000000000", + "ff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); + push_two_pop_one_constantinople_test( + &factory, + 0x1d, + "8000000000000000000000000000000000000000000000000000000000000000", + "0100", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); + push_two_pop_one_constantinople_test( + &factory, + 0x1d, + "8000000000000000000000000000000000000000000000000000000000000000", + "0101", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); + push_two_pop_one_constantinople_test( + &factory, + 0x1d, + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "00", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); + push_two_pop_one_constantinople_test( + &factory, + 0x1d, + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "01", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); + push_two_pop_one_constantinople_test( + &factory, + 0x1d, + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); + push_two_pop_one_constantinople_test( + &factory, + 0x1d, + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "0100", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); + push_two_pop_one_constantinople_test( + &factory, + 0x1d, + "0000000000000000000000000000000000000000000000000000000000000000", + "01", + "0000000000000000000000000000000000000000000000000000000000000000"); + push_two_pop_one_constantinople_test( + &factory, + 0x1d, + "4000000000000000000000000000000000000000000000000000000000000000", + "fe", + "0000000000000000000000000000000000000000000000000000000000000001"); + push_two_pop_one_constantinople_test( + &factory, + 0x1d, + "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "f8", + "000000000000000000000000000000000000000000000000000000000000007f"); + push_two_pop_one_constantinople_test( + &factory, + 0x1d, + "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "fe", + "0000000000000000000000000000000000000000000000000000000000000001"); + push_two_pop_one_constantinople_test( + &factory, + 0x1d, + "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ff", + "0000000000000000000000000000000000000000000000000000000000000000"); + push_two_pop_one_constantinople_test( + &factory, + 0x1d, + "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "0100", + "0000000000000000000000000000000000000000000000000000000000000000"); +} + +fn push_two_pop_one_constantinople_test(factory: &super::Factory, opcode: u8, push1: &str, push2: &str, result: &str) { + let mut push1 = push1.from_hex().unwrap(); + let mut push2 = push2.from_hex().unwrap(); + assert!(push1.len() <= 32 && push1.len() != 0); + assert!(push2.len() <= 32 && push2.len() != 0); + + let mut code = Vec::new(); + code.push(0x60 + ((push1.len() - 1) as u8)); + code.append(&mut push1); + code.push(0x60 + ((push2.len() - 1) as u8)); + code.append(&mut push2); + code.push(opcode); + code.append(&mut vec![0x60, 0x00, 0x55]); + + let mut params = ActionParams::default(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new_constantinople(); + + let _ = { + let mut vm = factory.create(¶ms.gas); + test_finalize(vm.exec(params, &mut ext)).unwrap() + }; + + assert_store(&ext, 0, result); +} + fn assert_set_contains(set: &HashSet, val: &T) { let contains = set.contains(val); if !contains { @@ -799,4 +1066,3 @@ fn assert_set_contains(set: &HashSet, val: fn assert_store(ext: &FakeExt, pos: u64, val: &str) { assert_eq!(ext.store.get(&H256::from(pos)).unwrap(), &H256::from_str(val).unwrap()); } - diff --git a/ethcore/evm/src/vmtype.rs b/ethcore/evm/src/vmtype.rs index 608ab1e815cd83cf604580720a30fd001bdddb05..feb567b73d9a9127df863e346db65bd96e27c8ee 100644 --- a/ethcore/evm/src/vmtype.rs +++ b/ethcore/evm/src/vmtype.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -19,22 +19,11 @@ use std::fmt; /// Type of EVM to use. #[derive(Debug, PartialEq, Clone)] pub enum VMType { - /// JIT EVM - #[cfg(feature = "jit")] - Jit, /// RUST EVM Interpreter } impl fmt::Display for VMType { - #[cfg(feature="jit")] - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", match *self { - VMType::Jit => "JIT", - VMType::Interpreter => "INT" - }) - } - #[cfg(not(feature="jit"))] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", match *self { VMType::Interpreter => "INT" @@ -49,27 +38,8 @@ impl Default for VMType { } impl VMType { - /// Return all possible VMs (JIT, Interpreter) - #[cfg(feature = "jit")] - pub fn all() -> Vec { - vec![VMType::Jit, VMType::Interpreter] - } - /// Return all possible VMs (Interpreter) - #[cfg(not(feature = "jit"))] pub fn all() -> Vec { vec![VMType::Interpreter] } - - /// Return new jit if it's possible - #[cfg(not(feature = "jit"))] - pub fn jit() -> Option { - None - } - - /// Return new jit if it's possible - #[cfg(feature = "jit")] - pub fn jit() -> Option { - Some(VMType::Jit) - } } diff --git a/ethcore/light/Cargo.toml b/ethcore/light/Cargo.toml index b8f5d5fb541837a67aaf5f9a49f35fe8d498dd25..ab347c62e837fd8487106075d6c477c1ea98da3d 100644 --- a/ethcore/light/Cargo.toml +++ b/ethcore/light/Cargo.toml @@ -3,7 +3,7 @@ description = "Parity Light Client Implementation" homepage = "http://parity.io" license = "GPL-3.0" name = "ethcore-light" -version = "1.11.0" +version = "1.12.0" authors = ["Parity Technologies "] [dependencies] @@ -11,7 +11,7 @@ log = "0.3" ethcore = { path = ".."} ethcore-bytes = { path = "../../util/bytes" } ethcore-transaction = { path = "../transaction" } -ethereum-types = "0.2" +ethereum-types = "0.3" memorydb = { path = "../../util/memorydb" } patricia-trie = { path = "../../util/patricia_trie" } ethcore-network = { path = "../../util/network" } @@ -35,8 +35,10 @@ keccak-hash = { path = "../../util/hash" } triehash = { path = "../../util/triehash" } kvdb = { path = "../../util/kvdb" } memory-cache = { path = "../../util/memory_cache" } +error-chain = { version = "0.11", default-features = false } [dev-dependencies] +ethcore = { path = "..", features = ["test-helpers"] } kvdb-memorydb = { path = "../../util/kvdb-memorydb" } tempdir = "0.3" diff --git a/ethcore/light/src/cache.rs b/ethcore/light/src/cache.rs index b63fd07576e005fd92b7058fdee045b34418aaca..7b6324a9912cd4b5605153b09103c4c86cbf9141 100644 --- a/ethcore/light/src/cache.rs +++ b/ethcore/light/src/cache.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/light/src/cht.rs b/ethcore/light/src/cht.rs index 6f5ebf808e886ce380e7b3bbac0a01171b3b51bc..805cca3cbc9a8573e49024ff0594d2eed8da1127 100644 --- a/ethcore/light/src/cht.rs +++ b/ethcore/light/src/cht.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -11,6 +11,9 @@ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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 . + //! Canonical hash trie definitions and helper functions. //! //! Each CHT is a trie mapping block numbers to canonical hashes and total difficulty. @@ -26,7 +29,7 @@ use hashdb::HashDB; use memorydb::MemoryDB; use bytes::Bytes; use trie::{self, TrieMut, TrieDBMut, Trie, TrieDB, Recorder}; -use rlp::{RlpStream, UntrustedRlp}; +use rlp::{RlpStream, Rlp}; // encode a key. macro_rules! key { @@ -150,7 +153,7 @@ pub fn check_proof(proof: &[Bytes], num: u64, root: H256) -> Option<(H256, U256) let res = match TrieDB::new(&db, &root) { Err(_) => return None, Ok(trie) => trie.get_with(&key!(num), |val: &[u8]| { - let rlp = UntrustedRlp::new(val); + let rlp = Rlp::new(val); rlp.val_at::(0) .and_then(|h| rlp.val_at::(1).map(|td| (h, td))) .ok() diff --git a/ethcore/light/src/client/fetch.rs b/ethcore/light/src/client/fetch.rs index 86269c695fc4b6aa422a84cd5987410b52e0d7a2..b0f73534962d1ef67584b55099b9cc7f3fffef32 100644 --- a/ethcore/light/src/client/fetch.rs +++ b/ethcore/light/src/client/fetch.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/light/src/client/header_chain.rs b/ethcore/light/src/client/header_chain.rs index 3bbd6cb9506a15cc75227d215f64485e27c670ee..cb370da2a7589b9936d56f43bb706aa73edaeef8 100644 --- a/ethcore/light/src/client/header_chain.rs +++ b/ethcore/light/src/client/header_chain.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -31,7 +31,7 @@ use std::sync::Arc; use cht; use ethcore::block_status::BlockStatus; -use ethcore::error::{Error, BlockImportError, BlockError}; +use ethcore::error::{Error, ErrorKind, BlockImportError, BlockImportErrorKind, BlockError}; use ethcore::encoded; use ethcore::header::Header; use ethcore::ids::BlockId; @@ -41,7 +41,7 @@ use ethcore::engines::epoch::{ PendingTransition as PendingEpochTransition }; -use rlp::{Encodable, Decodable, DecoderError, RlpStream, Rlp, UntrustedRlp}; +use rlp::{Encodable, Decodable, DecoderError, RlpStream, Rlp}; use heapsize::HeapSizeOf; use ethereum_types::{H256, H264, U256}; use plain_hasher::H256FastMap; @@ -74,6 +74,22 @@ pub struct BlockDescriptor { pub total_difficulty: U256, } +// best block data +#[derive(RlpEncodable, RlpDecodable)] +struct BestAndLatest { + best_num: u64, + latest_num: u64 +} + +impl BestAndLatest { + fn new(best_num: u64, latest_num: u64) -> Self { + BestAndLatest { + best_num, + latest_num, + } + } +} + // candidate block description. struct Candidate { hash: H256, @@ -109,7 +125,7 @@ impl Encodable for Entry { } impl Decodable for Entry { - fn decode(rlp: &UntrustedRlp) -> Result { + fn decode(rlp: &Rlp) -> Result { let mut candidates = SmallVec::<[Candidate; 3]>::new(); for item in rlp.iter() { @@ -170,7 +186,7 @@ fn encode_canonical_transition(header: &Header, proof: &[u8]) -> Vec { // decode last canonical transition entry. fn decode_canonical_transition(t: &[u8]) -> Result<(Header, &[u8]), DecoderError> { - let rlp = UntrustedRlp::new(t); + let rlp = Rlp::new(t); Ok((rlp.val_at(0)?, rlp.at(1)?.data()?)) } @@ -212,18 +228,15 @@ impl HeaderChain { let decoded_header = spec.genesis_header(); let chain = if let Some(current) = db.get(col, CURRENT_KEY)? { - let (best_number, highest_number) = { - let rlp = Rlp::new(¤t); - (rlp.val_at(0), rlp.val_at(1)) - }; + let curr : BestAndLatest = ::rlp::decode(¤t).expect("decoding db value failed"); - let mut cur_number = highest_number; + let mut cur_number = curr.latest_num; let mut candidates = BTreeMap::new(); // load all era entries, referenced headers within them, // and live epoch proofs. while let Some(entry) = db.get(col, era_key(cur_number).as_bytes())? { - let entry: Entry = ::rlp::decode(&entry); + let entry: Entry = ::rlp::decode(&entry).expect("decoding db value failed"); trace!(target: "chain", "loaded header chain entry for era {} with {} candidates", cur_number, entry.candidates.len()); @@ -245,15 +258,15 @@ impl HeaderChain { // fill best block block descriptor. let best_block = { - let era = match candidates.get(&best_number) { + let era = match candidates.get(&curr.best_num) { Some(era) => era, - None => return Err(Error::Database("Database corrupt: highest block referenced but no data.".into())), + None => bail!(ErrorKind::Database("Database corrupt: highest block referenced but no data.".into())), }; let best = &era.candidates[0]; BlockDescriptor { hash: best.hash, - number: best_number, + number: curr.best_num, total_difficulty: best.total_difficulty, } }; @@ -292,7 +305,7 @@ impl HeaderChain { batch.put(col, cht_key(cht_num as u64).as_bytes(), &::rlp::encode(cht_root)); } - let decoded_header = hardcoded_sync.header.decode(); + let decoded_header = hardcoded_sync.header.decode()?; let decoded_header_num = decoded_header.number(); // write the block in the DB. @@ -319,8 +332,7 @@ impl HeaderChain { // instantiate genesis epoch data if it doesn't exist. if let None = chain.db.get(col, LAST_CANONICAL_TRANSITION)? { - let genesis_data = spec.genesis_epoch_data() - .map_err(|s| Error::Database(s.into()))?; + let genesis_data = spec.genesis_epoch_data()?; { let mut batch = chain.db.transaction(); @@ -398,7 +410,7 @@ impl HeaderChain { .and_then(|entry| entry.candidates.iter().find(|c| c.hash == parent_hash)) .map(|c| c.total_difficulty) .ok_or_else(|| BlockError::UnknownParent(parent_hash)) - .map_err(BlockImportError::Block)? + .map_err(BlockImportErrorKind::Block)? }; parent_td + *header.difficulty() @@ -433,6 +445,7 @@ impl HeaderChain { let raw = header.encoded().into_inner(); transaction.put_vec(self.col, &hash[..], raw); + // TODO: For engines when required, use cryptoeconomic guarantees. let (best_num, is_new_best) = { let cur_best = self.best_block.read(); if cur_best.total_difficulty < total_difficulty { @@ -512,7 +525,10 @@ impl HeaderChain { None } Ok(None) => panic!("stored candidates always have corresponding headers; qed"), - Ok(Some(header)) => Some((epoch_transition, ::rlp::decode(&header))), + Ok(Some(header)) => Some(( + epoch_transition, + ::rlp::decode(&header).expect("decoding value from db failed") + )), }; } } @@ -542,9 +558,8 @@ impl HeaderChain { // write the best and latest eras to the database. { let latest_num = *candidates.iter().rev().next().expect("at least one era just inserted; qed").0; - let mut stream = RlpStream::new_list(2); - stream.append(&best_num).append(&latest_num); - transaction.put(self.col, CURRENT_KEY, &stream.out()) + let curr = BestAndLatest::new(best_num, latest_num); + transaction.put(self.col, CURRENT_KEY, &::rlp::encode(&curr)) } Ok(pending) } @@ -568,19 +583,19 @@ impl HeaderChain { } else { let msg = format!("header of block #{} not found in DB ; database in an \ inconsistent state", h_num); - return Err(Error::Database(msg.into())); + bail!(ErrorKind::Database(msg.into())); }; - let decoded = header.decode(); + let decoded = header.decode().expect("decoding db value failed"); let entry: Entry = { let bytes = self.db.get(self.col, era_key(h_num).as_bytes())? .ok_or_else(|| { let msg = format!("entry for era #{} not found in DB ; database \ in an inconsistent state", h_num); - Error::Database(msg.into()) + ErrorKind::Database(msg.into()) })?; - ::rlp::decode(&bytes) + ::rlp::decode(&bytes).expect("decoding db value failed") }; let total_difficulty = entry.candidates.iter() @@ -588,14 +603,14 @@ impl HeaderChain { .ok_or_else(|| { let msg = "no candidate matching block found in DB ; database in an \ inconsistent state"; - Error::Database(msg.into()) + ErrorKind::Database(msg.into()) })? .total_difficulty; break Ok(Some(SpecHardcodedSync { - header: header, - total_difficulty: total_difficulty, - chts: chts, + header, + total_difficulty, + chts, })); }, None => { @@ -731,7 +746,7 @@ impl HeaderChain { /// so including it within a CHT would be redundant. pub fn cht_root(&self, n: usize) -> Option { match self.db.get(self.col, cht_key(n as u64).as_bytes()) { - Ok(val) => val.map(|x| ::rlp::decode(&x)), + Ok(db_fetch) => db_fetch.map(|bytes| ::rlp::decode(&bytes).expect("decoding value from db failed")), Err(e) => { warn!(target: "chain", "Error reading from database: {}", e); None @@ -782,7 +797,7 @@ impl HeaderChain { pub fn pending_transition(&self, hash: H256) -> Option { let key = pending_transition_key(hash); match self.db.get(self.col, &*key) { - Ok(val) => val.map(|x| ::rlp::decode(&x)), + Ok(db_fetch) => db_fetch.map(|bytes| ::rlp::decode(&bytes).expect("decoding value from db failed")), Err(e) => { warn!(target: "chain", "Error reading from database: {}", e); None @@ -801,7 +816,9 @@ impl HeaderChain { for hdr in self.ancestry_iter(BlockId::Hash(parent_hash)) { if let Some(transition) = live_proofs.get(&hdr.hash()).cloned() { - return Some((hdr.decode(), transition.proof)) + return hdr.decode().map(|decoded_hdr| { + (decoded_hdr, transition.proof) + }).ok(); } } @@ -1181,7 +1198,7 @@ mod tests { let cache = Arc::new(Mutex::new(Cache::new(Default::default(), Duration::from_secs(6 * 3600)))); - let chain = HeaderChain::new(db.clone(), None, &spec, cache, HardcodedSync::Allow).unwrap(); + let chain = HeaderChain::new(db.clone(), None, &spec, cache, HardcodedSync::Allow).expect("failed to instantiate a new HeaderChain"); let mut parent_hash = genesis_header.hash(); let mut rolling_timestamp = genesis_header.timestamp(); @@ -1200,17 +1217,17 @@ mod tests { parent_hash = header.hash(); let mut tx = db.transaction(); - let pending = chain.insert(&mut tx, header, None).unwrap(); + let pending = chain.insert(&mut tx, header, None).expect("failed inserting a transaction"); db.write(tx).unwrap(); chain.apply_pending(pending); rolling_timestamp += 10; } - let hardcoded_sync = chain.read_hardcoded_sync().unwrap().unwrap(); + let hardcoded_sync = chain.read_hardcoded_sync().expect("failed reading hardcoded sync").expect("failed unwrapping hardcoded sync"); assert_eq!(hardcoded_sync.chts.len(), 3); assert_eq!(hardcoded_sync.total_difficulty, total_difficulty); - let decoded: Header = hardcoded_sync.header.decode(); + let decoded: Header = hardcoded_sync.header.decode().expect("decoding failed"); assert_eq!(decoded.number(), h_num); } } diff --git a/ethcore/light/src/client/mod.rs b/ethcore/light/src/client/mod.rs index cf603d853f07bbe118cf30443a53e7fc87e2511e..a1625b0e8f3ac1f8fcb1c570b809d03488639750 100644 --- a/ethcore/light/src/client/mod.rs +++ b/ethcore/light/src/client/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -318,7 +318,7 @@ impl Client { let epoch_proof = self.engine.is_epoch_end( &verified_header, - &|h| self.chain.block_header(BlockId::Hash(h)).map(|hdr| hdr.decode()), + &|h| self.chain.block_header(BlockId::Hash(h)).and_then(|hdr| hdr.decode().ok()), &|h| self.chain.pending_transition(h), ); @@ -426,7 +426,15 @@ impl Client { }; // Verify Block Family - let verify_family_result = self.engine.verify_block_family(&verified_header, &parent_header.decode()); + + let verify_family_result = { + parent_header.decode() + .map_err(|dec_err| dec_err.into()) + .and_then(|decoded| { + self.engine.verify_block_family(&verified_header, &decoded) + }) + + }; if let Err(e) = verify_family_result { warn!(target: "client", "Stage 3 block verification failed for #{} ({})\nError: {:?}", verified_header.number(), verified_header.hash(), e); @@ -455,7 +463,6 @@ impl Client { loop { - let is_signal = { let auxiliary = AuxiliaryData { bytes: block.as_ref().map(|x| &x[..]), diff --git a/ethcore/light/src/client/service.rs b/ethcore/light/src/client/service.rs index 3d13be16b4a8f0ab72b8b5412e0d3a75e97ec9b4..d1645cfe7e86f3918bb4d3e91a1d2fc20273ed3e 100644 --- a/ethcore/light/src/client/service.rs +++ b/ethcore/light/src/client/service.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -30,7 +30,7 @@ use kvdb::KeyValueDB; use cache::Cache; use parking_lot::Mutex; -use super::{ChainDataFetcher, Client, Config as ClientConfig}; +use super::{ChainDataFetcher, LightChainNotify, Client, Config as ClientConfig}; /// Errors on service initialization. #[derive(Debug)] @@ -86,6 +86,11 @@ impl Service { }) } + /// Set the actor to be notified on certain chain events + pub fn add_notify(&self, notify: Arc) { + self.client.add_listener(Arc::downgrade(¬ify)); + } + /// Register an I/O handler on the service. pub fn register_handler(&self, handler: Arc + Send>) -> Result<(), IoError> { self.io_service.register_handler(handler) diff --git a/ethcore/light/src/lib.rs b/ethcore/light/src/lib.rs index 1ffe0079c18ae398661ec969abd51d04c5be1c69..d7469fdcee00c79814fa8cc3fe270a889d71bd47 100644 --- a/ethcore/light/src/lib.rs +++ b/ethcore/light/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -80,6 +80,8 @@ extern crate keccak_hash as hash; extern crate triehash; extern crate kvdb; extern crate memory_cache; +#[macro_use] +extern crate error_chain; #[cfg(test)] extern crate kvdb_memorydb; diff --git a/ethcore/light/src/net/context.rs b/ethcore/light/src/net/context.rs index 613e26b1f1190e7829bd570615c6cb369f0f3996..a49ef79dc241e7631b40e6bab2573bb6a2b642ed 100644 --- a/ethcore/light/src/net/context.rs +++ b/ethcore/light/src/net/context.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -12,7 +12,7 @@ // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity. If not, see . +// along with Parity. If not, see . //! I/O and event context generalizations. @@ -46,7 +46,6 @@ pub trait IoContext { fn persistent_peer_id(&self, peer: PeerId) -> Option; } - impl IoContext for T where T: ?Sized + NetworkContext { fn send(&self, peer: PeerId, packet_id: u8, packet_body: Vec) { if let Err(e) = self.send(peer, packet_id, packet_body) { diff --git a/ethcore/light/src/net/error.rs b/ethcore/light/src/net/error.rs index 35349c553925c7e0b396762333314576111d0d12..ec2a7f91c597df1ce52ed81e51d45457dcaa4ee8 100644 --- a/ethcore/light/src/net/error.rs +++ b/ethcore/light/src/net/error.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/light/src/net/load_timer.rs b/ethcore/light/src/net/load_timer.rs index c6e524a4412eca8142ff19aed7b6bb920d216825..0ad96270219508edbe9d0212e4a613648522aa09 100644 --- a/ethcore/light/src/net/load_timer.rs +++ b/ethcore/light/src/net/load_timer.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -48,11 +48,11 @@ pub trait SampleStore: Send + Sync { } // get a hardcoded, arbitrarily determined (but intended overestimate) -// of the time in nanoseconds to serve a request of the given kind. +// of the time it takes to serve a request of the given kind. // // TODO: seed this with empirical data. -fn hardcoded_serve_time(kind: Kind) -> u64 { - match kind { +fn hardcoded_serve_time(kind: Kind) -> Duration { + Duration::new(0, match kind { Kind::Headers => 500_000, Kind::HeaderProof => 500_000, Kind::TransactionIndex => 500_000, @@ -63,7 +63,7 @@ fn hardcoded_serve_time(kind: Kind) -> u64 { Kind::Code => 1_500_000, Kind::Execution => 250, // per gas. Kind::Signal => 500_000, - } + }) } /// A no-op store. @@ -114,10 +114,10 @@ impl LoadDistribution { } } - /// Calculate EMA of load in nanoseconds for a specific request kind. + /// Calculate EMA of load for a specific request kind. /// If there is no data for the given request kind, no EMA will be calculated, /// but a hardcoded time will be returned. - pub fn expected_time_ns(&self, kind: Kind) -> u64 { + pub fn expected_time(&self, kind: Kind) -> Duration { let samples = self.samples.read(); samples.get(&kind).and_then(|s| { if s.len() == 0 { return None } @@ -128,7 +128,9 @@ impl LoadDistribution { (alpha * c as f64) + ((1.0 - alpha) * a) }); - Some(ema as u64) + // TODO: use `Duration::from_nanos` once stable (https://github.com/rust-lang/rust/issues/46507) + let ema = ema as u64; + Some(Duration::new(ema / 1_000_000_000, (ema % 1_000_000_000) as u32)) }).unwrap_or_else(move || hardcoded_serve_time(kind)) } @@ -223,12 +225,12 @@ mod tests { #[test] fn hardcoded_before_data() { let dist = LoadDistribution::load(&NullStore); - assert_eq!(dist.expected_time_ns(Kind::Headers), hardcoded_serve_time(Kind::Headers)); + assert_eq!(dist.expected_time(Kind::Headers), hardcoded_serve_time(Kind::Headers)); dist.update(Kind::Headers, Duration::new(0, 100_000), 100); dist.end_period(&NullStore); - assert_eq!(dist.expected_time_ns(Kind::Headers), 1000); + assert_eq!(dist.expected_time(Kind::Headers), Duration::new(0, 1000)); } #[test] @@ -244,20 +246,20 @@ mod tests { sum += x; if i == 0 { continue } - let moving_average = dist.expected_time_ns(Kind::Headers); + let moving_average = dist.expected_time(Kind::Headers); // should be weighted below the maximum entry. - let arith_average = (sum as f64 / (i + 1) as f64) as u64; - assert!(moving_average < x as u64); + let arith_average = (sum as f64 / (i + 1) as f64) as u32; + assert!(moving_average < Duration::new(0, x)); // when there are only 2 entries, they should be equal due to choice of // ALPHA = 1/N. // otherwise, the weight should be below the arithmetic mean because the much // smaller previous values are discounted less. if i == 1 { - assert_eq!(moving_average, arith_average); + assert_eq!(moving_average, Duration::new(0, arith_average)); } else { - assert!(moving_average < arith_average) + assert!(moving_average < Duration::new(0, arith_average)) } } } diff --git a/ethcore/light/src/net/mod.rs b/ethcore/light/src/net/mod.rs index 60fe172e23c4e30827bb14a0df9609dab6bd6536..179f7090036b75d40c602f3a8519518f4568a6e0 100644 --- a/ethcore/light/src/net/mod.rs +++ b/ethcore/light/src/net/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -21,8 +21,8 @@ use transaction::UnverifiedTransaction; use io::TimerToken; -use network::{HostInfo, NetworkProtocolHandler, NetworkContext, PeerId}; -use rlp::{RlpStream, UntrustedRlp}; +use network::{NetworkProtocolHandler, NetworkContext, PeerId}; +use rlp::{RlpStream, Rlp}; use ethereum_types::{H256, U256}; use kvdb::DBValue; use parking_lot::{Mutex, RwLock}; @@ -61,29 +61,34 @@ pub use self::load_timer::{SampleStore, FileStore}; pub use self::status::{Status, Capabilities, Announcement}; const TIMEOUT: TimerToken = 0; -const TIMEOUT_INTERVAL_MS: u64 = 1000; +const TIMEOUT_INTERVAL: Duration = Duration::from_secs(1); const TICK_TIMEOUT: TimerToken = 1; -const TICK_TIMEOUT_INTERVAL_MS: u64 = 5000; +const TICK_TIMEOUT_INTERVAL: Duration = Duration::from_secs(5); const PROPAGATE_TIMEOUT: TimerToken = 2; -const PROPAGATE_TIMEOUT_INTERVAL_MS: u64 = 5000; +const PROPAGATE_TIMEOUT_INTERVAL: Duration = Duration::from_secs(5); const RECALCULATE_COSTS_TIMEOUT: TimerToken = 3; -const RECALCULATE_COSTS_INTERVAL_MS: u64 = 60 * 60 * 1000; +const RECALCULATE_COSTS_INTERVAL: Duration = Duration::from_secs(60 * 60); + +/// Max number of transactions in a single packet. +const MAX_TRANSACTIONS_TO_PROPAGATE: usize = 64; // minimum interval between updates. -const UPDATE_INTERVAL_MS: u64 = 5000; +const UPDATE_INTERVAL: Duration = Duration::from_millis(5000); + +/// Packet count for PIP. +const PACKET_COUNT_V1: u8 = 9; /// Supported protocol versions. -pub const PROTOCOL_VERSIONS: &'static [u8] = &[1]; +pub const PROTOCOL_VERSIONS: &'static [(u8, u8)] = &[ + (1, PACKET_COUNT_V1), +]; /// Max protocol version. pub const MAX_PROTOCOL_VERSION: u8 = 1; -/// Packet count for PIP. -pub const PACKET_COUNT: u8 = 9; - // packet ID definitions. mod packet { // the status packet. @@ -109,9 +114,11 @@ mod packet { // timeouts for different kinds of requests. all values are in milliseconds. mod timeout { - pub const HANDSHAKE: u64 = 2500; - pub const ACKNOWLEDGE_UPDATE: u64 = 5000; - pub const BASE: u64 = 1500; // base timeout for packet. + use std::time::Duration; + + pub const HANDSHAKE: Duration = Duration::from_millis(4_000); + pub const ACKNOWLEDGE_UPDATE: Duration = Duration::from_millis(5_000); + pub const BASE: u64 = 2_500; // base timeout for packet. // timeouts per request within packet. pub const HEADERS: u64 = 250; // per header? @@ -367,9 +374,9 @@ impl LightProtocol { let sample_store = params.sample_store.unwrap_or_else(|| Box::new(NullStore)); let load_distribution = LoadDistribution::load(&*sample_store); let flow_params = FlowParams::from_request_times( - |kind| load_distribution.expected_time_ns(kind), + |kind| load_distribution.expected_time(kind), params.config.load_share, - params.config.max_stored_seconds, + Duration::from_secs(params.config.max_stored_seconds), ); LightProtocol { @@ -470,7 +477,7 @@ impl LightProtocol { // the timer approach will skip 1 (possibly 2) in rare occasions. if peer_info.sent_head == announcement.head_hash || peer_info.status.head_num >= announcement.head_num || - now - peer_info.last_update < Duration::from_millis(UPDATE_INTERVAL_MS) { + now - peer_info.last_update < UPDATE_INTERVAL { continue } @@ -526,7 +533,7 @@ impl LightProtocol { // - check whether peer exists // - check whether request was made // - check whether request kinds match - fn pre_verify_response(&self, peer: &PeerId, raw: &UntrustedRlp) -> Result { + fn pre_verify_response(&self, peer: &PeerId, raw: &Rlp) -> Result { let req_id = ReqId(raw.val_at(0)?); let cur_credits: U256 = raw.val_at(1)?; @@ -570,7 +577,7 @@ impl LightProtocol { /// Packet data is _untrusted_, which means that invalid data won't lead to /// issues. pub fn handle_packet(&self, io: &IoContext, peer: &PeerId, packet_id: u8, data: &[u8]) { - let rlp = UntrustedRlp::new(data); + let rlp = Rlp::new(data); trace!(target: "pip", "Incoming packet {} from peer {}", packet_id, peer); @@ -606,7 +613,7 @@ impl LightProtocol { let mut pending = self.pending_peers.write(); let slowpokes: Vec<_> = pending.iter() .filter(|&(_, ref peer)| { - peer.last_update + Duration::from_millis(timeout::HANDSHAKE) <= now + peer.last_update + timeout::HANDSHAKE <= now }) .map(|(&p, _)| p) .collect(); @@ -619,7 +626,7 @@ impl LightProtocol { } // request and update ack timeouts - let ack_duration = Duration::from_millis(timeout::ACKNOWLEDGE_UPDATE); + let ack_duration = timeout::ACKNOWLEDGE_UPDATE; { for (peer_id, peer) in self.peers.read().iter() { let peer = peer.lock(); @@ -643,7 +650,7 @@ impl LightProtocol { fn propagate_transactions(&self, io: &IoContext) { if self.capabilities.read().tx_relay { return } - let ready_transactions = self.provider.ready_transactions(); + let ready_transactions = self.provider.ready_transactions(MAX_TRANSACTIONS_TO_PROPAGATE); if ready_transactions.is_empty() { return } trace!(target: "pip", "propagate transactions: {} ready", ready_transactions.len()); @@ -686,7 +693,7 @@ impl LightProtocol { Err(e) => { punish(*peer, io, e); return } }; - if PROTOCOL_VERSIONS.iter().find(|x| **x == proto_version).is_none() { + if PROTOCOL_VERSIONS.iter().find(|x| x.0 == proto_version).is_none() { punish(*peer, io, Error::UnsupportedProtocolVersion(proto_version)); return; } @@ -764,9 +771,9 @@ impl LightProtocol { self.load_distribution.end_period(&*self.sample_store); let new_params = Arc::new(FlowParams::from_request_times( - |kind| self.load_distribution.expected_time_ns(kind), + |kind| self.load_distribution.expected_time(kind), self.config.load_share, - self.config.max_stored_seconds, + Duration::from_secs(self.config.max_stored_seconds), )); *self.flow_params.write() = new_params.clone(); @@ -792,7 +799,7 @@ impl LightProtocol { impl LightProtocol { // Handle status message from peer. - fn status(&self, peer: &PeerId, io: &IoContext, data: UntrustedRlp) -> Result<(), Error> { + fn status(&self, peer: &PeerId, io: &IoContext, data: Rlp) -> Result<(), Error> { let pending = match self.pending_peers.write().remove(peer) { Some(pending) => pending, None => { @@ -853,7 +860,7 @@ impl LightProtocol { } // Handle an announcement. - fn announcement(&self, peer: &PeerId, io: &IoContext, data: UntrustedRlp) -> Result<(), Error> { + fn announcement(&self, peer: &PeerId, io: &IoContext, data: Rlp) -> Result<(), Error> { if !self.peers.read().contains_key(peer) { debug!(target: "pip", "Ignoring announcement from unknown peer"); return Ok(()) @@ -898,7 +905,7 @@ impl LightProtocol { } // Receive requests from a peer. - fn request(&self, peer_id: &PeerId, io: &IoContext, raw: UntrustedRlp) -> Result<(), Error> { + fn request(&self, peer_id: &PeerId, io: &IoContext, raw: Rlp) -> Result<(), Error> { // the maximum amount of requests we'll fill in a single packet. const MAX_REQUESTS: usize = 256; @@ -966,7 +973,7 @@ impl LightProtocol { } // handle a packet with responses. - fn response(&self, peer: &PeerId, io: &IoContext, raw: UntrustedRlp) -> Result<(), Error> { + fn response(&self, peer: &PeerId, io: &IoContext, raw: Rlp) -> Result<(), Error> { let (req_id, responses) = { let id_guard = self.pre_verify_response(peer, &raw)?; let responses: Vec = raw.list_at(2)?; @@ -985,7 +992,7 @@ impl LightProtocol { } // handle an update of request credits parameters. - fn update_credits(&self, peer_id: &PeerId, io: &IoContext, raw: UntrustedRlp) -> Result<(), Error> { + fn update_credits(&self, peer_id: &PeerId, io: &IoContext, raw: Rlp) -> Result<(), Error> { let peers = self.peers.read(); let peer = peers.get(peer_id).ok_or(Error::UnknownPeer)?; @@ -1020,7 +1027,7 @@ impl LightProtocol { } // handle an acknowledgement of request credits update. - fn acknowledge_update(&self, peer_id: &PeerId, _io: &IoContext, _raw: UntrustedRlp) -> Result<(), Error> { + fn acknowledge_update(&self, peer_id: &PeerId, _io: &IoContext, _raw: Rlp) -> Result<(), Error> { let peers = self.peers.read(); let peer = peers.get(peer_id).ok_or(Error::UnknownPeer)?; let mut peer = peer.lock(); @@ -1039,7 +1046,7 @@ impl LightProtocol { } // Receive a set of transactions to relay. - fn relay_transactions(&self, peer: &PeerId, io: &IoContext, data: UntrustedRlp) -> Result<(), Error> { + fn relay_transactions(&self, peer: &PeerId, io: &IoContext, data: Rlp) -> Result<(), Error> { const MAX_TRANSACTIONS: usize = 256; let txs: Vec<_> = data.iter() @@ -1077,14 +1084,14 @@ fn punish(peer: PeerId, io: &IoContext, e: Error) { } impl NetworkProtocolHandler for LightProtocol { - fn initialize(&self, io: &NetworkContext, _host_info: &HostInfo) { - io.register_timer(TIMEOUT, TIMEOUT_INTERVAL_MS) + fn initialize(&self, io: &NetworkContext) { + io.register_timer(TIMEOUT, TIMEOUT_INTERVAL) .expect("Error registering sync timer."); - io.register_timer(TICK_TIMEOUT, TICK_TIMEOUT_INTERVAL_MS) + io.register_timer(TICK_TIMEOUT, TICK_TIMEOUT_INTERVAL) .expect("Error registering sync timer."); - io.register_timer(PROPAGATE_TIMEOUT, PROPAGATE_TIMEOUT_INTERVAL_MS) + io.register_timer(PROPAGATE_TIMEOUT, PROPAGATE_TIMEOUT_INTERVAL) .expect("Error registering sync timer."); - io.register_timer(RECALCULATE_COSTS_TIMEOUT, RECALCULATE_COSTS_INTERVAL_MS) + io.register_timer(RECALCULATE_COSTS_TIMEOUT, RECALCULATE_COSTS_INTERVAL) .expect("Error registering request timer interval token."); } diff --git a/ethcore/light/src/net/request_credits.rs b/ethcore/light/src/net/request_credits.rs index f97ce85bcdfd911ddc0413bf2f5c9c1a90fcb8c3..e97e1aad58aeec3b5e1313effb2f9b9a9bfc6043 100644 --- a/ethcore/light/src/net/request_credits.rs +++ b/ethcore/light/src/net/request_credits.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -29,7 +29,7 @@ use request::{self, Request}; use super::error::Error; -use rlp::*; +use rlp::{Rlp, RlpStream, Decodable, Encodable, DecoderError}; use ethereum_types::U256; use std::time::{Duration, Instant}; @@ -162,7 +162,7 @@ impl Encodable for CostTable { } impl Decodable for CostTable { - fn decode(rlp: &UntrustedRlp) -> Result { + fn decode(rlp: &Rlp) -> Result { let base = rlp.val_at(0)?; let mut headers = None; @@ -235,23 +235,30 @@ impl FlowParams { /// Create new flow parameters from , /// proportion of total capacity which should be given to a peer, - /// and number of seconds of stored capacity a peer can accumulate. - pub fn from_request_times u64>( - request_time_ns: F, + /// and stored capacity a peer can accumulate. + pub fn from_request_times Duration>( + request_time: F, load_share: f64, - max_stored_seconds: u64 + max_stored: Duration ) -> Self { use request::Kind; let load_share = load_share.abs(); let recharge: u64 = 100_000_000; - let max = recharge.saturating_mul(max_stored_seconds); + let max = { + let sec = max_stored.as_secs().saturating_mul(recharge); + let nanos = (max_stored.subsec_nanos() as u64).saturating_mul(recharge) / 1_000_000_000; + sec + nanos + }; let cost_for_kind = |kind| { // how many requests we can handle per second - let ns = request_time_ns(kind); - let second_duration = 1_000_000_000f64 / ns as f64; + let rq_dur = request_time(kind); + let second_duration = { + let as_ns = rq_dur.as_secs() as f64 * 1_000_000_000f64 + rq_dur.subsec_nanos() as f64; + 1_000_000_000f64 / as_ns + }; // scale by share of the load given to this peer. let serve_per_second = second_duration * load_share; @@ -400,7 +407,7 @@ mod tests { let costs = CostTable::default(); let serialized = ::rlp::encode(&costs); - let new_costs: CostTable = ::rlp::decode(&*serialized); + let new_costs: CostTable = ::rlp::decode(&*serialized).unwrap(); assert_eq!(costs, new_costs); } @@ -426,21 +433,21 @@ mod tests { #[test] fn scale_by_load_share_and_time() { let flow_params = FlowParams::from_request_times( - |_| 10_000, + |_| Duration::new(0, 10_000), 0.05, - 60, + Duration::from_secs(60), ); let flow_params2 = FlowParams::from_request_times( - |_| 10_000, + |_| Duration::new(0, 10_000), 0.1, - 60, + Duration::from_secs(60), ); let flow_params3 = FlowParams::from_request_times( - |_| 5_000, + |_| Duration::new(0, 5_000), 0.05, - 60, + Duration::from_secs(60), ); assert_eq!(flow_params2.costs, flow_params3.costs); diff --git a/ethcore/light/src/net/request_set.rs b/ethcore/light/src/net/request_set.rs index 27e6c28bc2d1171b5cd3bf3f0f0c4bb78a3c752f..4170f8e6328215943f9e0f8cd5f57d8da95a39ec 100644 --- a/ethcore/light/src/net/request_set.rs +++ b/ethcore/light/src/net/request_set.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/light/src/net/status.rs b/ethcore/light/src/net/status.rs index 0f811bbe40845f96f340a0c13b6a17f8fbf71bec..d89db173cb8a156215050c354505a558f3f07d99 100644 --- a/ethcore/light/src/net/status.rs +++ b/ethcore/light/src/net/status.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -16,7 +16,7 @@ //! Peer status and capabilities. -use rlp::{DecoderError, Encodable, Decodable, RlpStream, UntrustedRlp}; +use rlp::{DecoderError, Encodable, Decodable, RlpStream, Rlp}; use ethereum_types::{H256, U256}; use super::request_credits::FlowParams; @@ -85,7 +85,7 @@ impl Key { // helper for decoding key-value pairs in the handshake or an announcement. struct Parser<'a> { pos: usize, - rlp: UntrustedRlp<'a>, + rlp: Rlp<'a>, } impl<'a> Parser<'a> { @@ -97,7 +97,7 @@ impl<'a> Parser<'a> { // expect a specific next key, and get the value's RLP. // if the key isn't found, the position isn't advanced. - fn expect_raw(&mut self, key: Key) -> Result, DecoderError> { + fn expect_raw(&mut self, key: Key) -> Result, DecoderError> { trace!(target: "les", "Expecting key {}", key.as_str()); let pre_pos = self.pos; if let Some((k, val)) = self.get_next()? { @@ -109,7 +109,7 @@ impl<'a> Parser<'a> { } // get the next key and value RLP. - fn get_next(&mut self) -> Result)>, DecoderError> { + fn get_next(&mut self) -> Result)>, DecoderError> { while self.pos < self.rlp.item_count()? { let pair = self.rlp.at(self.pos)?; let k: String = pair.val_at(0)?; @@ -208,7 +208,7 @@ impl Capabilities { /// - chain status /// - serving capabilities /// - request credit parameters -pub fn parse_handshake(rlp: UntrustedRlp) -> Result<(Status, Capabilities, Option), DecoderError> { +pub fn parse_handshake(rlp: Rlp) -> Result<(Status, Capabilities, Option), DecoderError> { let mut parser = Parser { pos: 0, rlp: rlp, @@ -304,7 +304,7 @@ pub struct Announcement { } /// Parse an announcement. -pub fn parse_announcement(rlp: UntrustedRlp) -> Result { +pub fn parse_announcement(rlp: Rlp) -> Result { let mut last_key = None; let mut announcement = Announcement { @@ -374,7 +374,7 @@ mod tests { use super::*; use super::super::request_credits::FlowParams; use ethereum_types::{U256, H256}; - use rlp::{RlpStream, UntrustedRlp}; + use rlp::{RlpStream, Rlp}; #[test] fn full_handshake() { @@ -404,7 +404,7 @@ mod tests { let handshake = write_handshake(&status, &capabilities, Some(&flow_params)); let (read_status, read_capabilities, read_flow) - = parse_handshake(UntrustedRlp::new(&handshake)).unwrap(); + = parse_handshake(Rlp::new(&handshake)).unwrap(); assert_eq!(read_status, status); assert_eq!(read_capabilities, capabilities); @@ -439,7 +439,7 @@ mod tests { let handshake = write_handshake(&status, &capabilities, Some(&flow_params)); let (read_status, read_capabilities, read_flow) - = parse_handshake(UntrustedRlp::new(&handshake)).unwrap(); + = parse_handshake(Rlp::new(&handshake)).unwrap(); assert_eq!(read_status, status); assert_eq!(read_capabilities, capabilities); @@ -473,7 +473,7 @@ mod tests { let handshake = write_handshake(&status, &capabilities, Some(&flow_params)); let interleaved = { - let handshake = UntrustedRlp::new(&handshake); + let handshake = Rlp::new(&handshake); let mut stream = RlpStream::new_list(handshake.item_count().unwrap_or(0) * 3); for item in handshake.iter() { @@ -489,7 +489,7 @@ mod tests { }; let (read_status, read_capabilities, read_flow) - = parse_handshake(UntrustedRlp::new(&interleaved)).unwrap(); + = parse_handshake(Rlp::new(&interleaved)).unwrap(); assert_eq!(read_status, status); assert_eq!(read_capabilities, capabilities); @@ -510,7 +510,7 @@ mod tests { }; let serialized = write_announcement(&announcement); - let read = parse_announcement(UntrustedRlp::new(&serialized)).unwrap(); + let read = parse_announcement(Rlp::new(&serialized)).unwrap(); assert_eq!(read, announcement); } @@ -529,7 +529,7 @@ mod tests { .append_raw(&encode_flag(Key::ServeHeaders), 1); let out = stream.drain(); - assert!(parse_announcement(UntrustedRlp::new(&out)).is_err()); + assert!(parse_announcement(Rlp::new(&out)).is_err()); let mut stream = RlpStream::new_list(6); stream @@ -541,7 +541,7 @@ mod tests { .append_raw(&encode_pair(Key::ServeStateSince, &44u64), 1); let out = stream.drain(); - assert!(parse_announcement(UntrustedRlp::new(&out)).is_ok()); + assert!(parse_announcement(Rlp::new(&out)).is_ok()); } #[test] @@ -566,7 +566,7 @@ mod tests { let handshake = write_handshake(&status, &capabilities, None); let (read_status, read_capabilities, read_flow) - = parse_handshake(UntrustedRlp::new(&handshake)).unwrap(); + = parse_handshake(Rlp::new(&handshake)).unwrap(); assert_eq!(read_status, status); assert_eq!(read_capabilities, capabilities); diff --git a/ethcore/light/src/net/tests/mod.rs b/ethcore/light/src/net/tests/mod.rs index c3c792b786a5be0122b864b018cd5995294c3e4d..e182f38b8a6c7fe03cd07f9c88a7fdb65dbd4271 100644 --- a/ethcore/light/src/net/tests/mod.rs +++ b/ethcore/light/src/net/tests/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -31,7 +31,7 @@ use provider::Provider; use request; use request::*; -use rlp::*; +use rlp::{Rlp, RlpStream}; use ethereum_types::{H256, U256, Address}; use std::sync::Arc; @@ -173,8 +173,8 @@ impl Provider for TestProvider { }) } - fn ready_transactions(&self) -> Vec { - self.0.client.ready_transactions() + fn ready_transactions(&self, max_len: usize) -> Vec { + self.0.client.ready_transactions(max_len) } } @@ -405,7 +405,7 @@ fn get_block_receipts() { // by the test client in that case. let block_hashes: Vec = (0..1000) .map(|i| provider.client.block_header(BlockId::Number(i)).unwrap().hash()) - .filter(|hash| format!("{}", hash).starts_with("f")) + .filter(|hash| format!("{}", hash).starts_with("0xf")) .take(10) .collect(); @@ -688,7 +688,7 @@ fn id_guard() { stream.begin_list(2).append(&125usize).append(&3usize); let packet = stream.out(); - assert!(proto.response(&peer_id, &Expect::Nothing, UntrustedRlp::new(&packet)).is_err()); + assert!(proto.response(&peer_id, &Expect::Nothing, Rlp::new(&packet)).is_err()); } // next, do an unexpected response. @@ -699,7 +699,7 @@ fn id_guard() { stream.begin_list(0); let packet = stream.out(); - assert!(proto.response(&peer_id, &Expect::Nothing, UntrustedRlp::new(&packet)).is_err()); + assert!(proto.response(&peer_id, &Expect::Nothing, Rlp::new(&packet)).is_err()); } // lastly, do a valid (but empty) response. @@ -710,7 +710,7 @@ fn id_guard() { stream.begin_list(0); let packet = stream.out(); - assert!(proto.response(&peer_id, &Expect::Nothing, UntrustedRlp::new(&packet)).is_ok()); + assert!(proto.response(&peer_id, &Expect::Nothing, Rlp::new(&packet)).is_ok()); } let peers = proto.peers.read(); diff --git a/ethcore/light/src/on_demand/mod.rs b/ethcore/light/src/on_demand/mod.rs index 64794d49e720c630060ab5c646a743e4359c3ef3..c7cc5ef5e7d5162455a1d8b24f5dda32aaaecf8c 100644 --- a/ethcore/light/src/on_demand/mod.rs +++ b/ethcore/light/src/on_demand/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/light/src/on_demand/request.rs b/ethcore/light/src/on_demand/request.rs index e7303ad884eb28e1780570f414e70f5c26624e5b..4cac6b629dbf32049815d7c780fc5952892416a4 100644 --- a/ethcore/light/src/on_demand/request.rs +++ b/ethcore/light/src/on_demand/request.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -30,7 +30,7 @@ use hash::{KECCAK_NULL_RLP, KECCAK_EMPTY, KECCAK_EMPTY_LIST_RLP, keccak}; use request::{self as net_request, IncompleteRequest, CompleteRequest, Output, OutputKind, Field}; -use rlp::{RlpStream, UntrustedRlp}; +use rlp::{RlpStream, Rlp}; use ethereum_types::{H256, U256, Address}; use parking_lot::Mutex; use hashdb::HashDB; @@ -439,13 +439,7 @@ impl CheckedRequest { block_header .and_then(|hdr| cache.block_body(&block_hash).map(|b| (hdr, b))) .map(|(hdr, body)| { - let mut stream = RlpStream::new_list(3); - let body = body.rlp(); - stream.append_raw(&hdr.rlp().as_raw(), 1); - stream.append_raw(&body.at(0).as_raw(), 1); - stream.append_raw(&body.at(1).as_raw(), 1); - - Response::Body(encoded::Block::new(stream.out())) + Response::Body(encoded::Block::new_from_header_and_body(&hdr.view(), &body.view())) }) } CheckedRequest::Code(_, ref req) => { @@ -526,7 +520,6 @@ impl IncompleteRequest for CheckedRequest { } } - fn adjust_refs(&mut self, mapping: F) where F: FnMut(usize) -> usize { match_me!(*self, (_, ref mut req) => req.adjust_refs(mapping)) } @@ -778,25 +771,22 @@ impl Body { pub fn check_response(&self, cache: &Mutex<::cache::Cache>, body: &encoded::Body) -> Result { // check the integrity of the the body against the header let header = self.0.as_ref()?; - let tx_root = ::triehash::ordered_trie_root(body.rlp().at(0).iter().map(|r| r.as_raw())); + let tx_root = ::triehash::ordered_trie_root(body.transactions_rlp().iter().map(|r| r.as_raw())); if tx_root != header.transactions_root() { return Err(Error::WrongTrieRoot(header.transactions_root(), tx_root)); } - let uncles_hash = keccak(body.rlp().at(1).as_raw()); + let uncles_hash = keccak(body.uncles_rlp().as_raw()); if uncles_hash != header.uncles_hash() { return Err(Error::WrongHash(header.uncles_hash(), uncles_hash)); } // concatenate the header and the body. - let mut stream = RlpStream::new_list(3); - stream.append_raw(header.rlp().as_raw(), 1); - stream.append_raw(body.rlp().at(0).as_raw(), 1); - stream.append_raw(body.rlp().at(1).as_raw(), 1); + let block = encoded::Block::new_from_header_and_body(&header.view(), &body.view()); cache.lock().insert_block_body(header.hash(), body.clone()); - Ok(encoded::Block::new(stream.out())) + Ok(block) } } @@ -840,7 +830,7 @@ impl Account { match TrieDB::new(&db, &state_root).and_then(|t| t.get(&keccak(&self.address)))? { Some(val) => { - let rlp = UntrustedRlp::new(&val); + let rlp = Rlp::new(&val); Ok(Some(BasicAccount { nonce: rlp.val_at(0)?, balance: rlp.val_at(1)?, diff --git a/ethcore/light/src/on_demand/tests.rs b/ethcore/light/src/on_demand/tests.rs index 999f2e669a5cecc1044135059f23a92a1b48ec82..d3cd137ec05663037e1822342590a2c12dde4db7 100644 --- a/ethcore/light/src/on_demand/tests.rs +++ b/ethcore/light/src/on_demand/tests.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -17,8 +17,7 @@ //! Tests for the on-demand service. use cache::Cache; -use ethcore::encoded; -use ethcore::header::{Header, Seal}; +use ethcore::header::Header; use futures::Future; use network::{PeerId, NodeId}; use net::*; @@ -148,7 +147,7 @@ fn single_request() { }); let header = Header::default(); - let encoded = encoded::Header::new(header.rlp(Seal::With)); + let encoded = header.encoded(); let recv = harness.service.request_raw( &Context::NoOp, @@ -209,7 +208,7 @@ fn reassign() { }); let header = Header::default(); - let encoded = encoded::Header::new(header.rlp(Seal::With)); + let encoded = header.encoded(); let recv = harness.service.request_raw( &Context::NoOp, @@ -257,7 +256,7 @@ fn partial_response() { let mut hdr = Header::default(); hdr.set_number(num); - let encoded = encoded::Header::new(hdr.rlp(Seal::With)); + let encoded = hdr.encoded(); (hdr, encoded) }; @@ -316,7 +315,7 @@ fn part_bad_part_good() { let mut hdr = Header::default(); hdr.set_number(num); - let encoded = encoded::Header::new(hdr.rlp(Seal::With)); + let encoded = hdr.encoded(); (hdr, encoded) }; @@ -413,7 +412,7 @@ fn back_references() { }); let header = Header::default(); - let encoded = encoded::Header::new(header.rlp(Seal::With)); + let encoded = header.encoded(); let recv = harness.service.request_raw( &Context::NoOp, @@ -470,7 +469,7 @@ fn fill_from_cache() { }); let header = Header::default(); - let encoded = encoded::Header::new(header.rlp(Seal::With)); + let encoded = header.encoded(); let recv = harness.service.request_raw( &Context::NoOp, diff --git a/ethcore/light/src/provider.rs b/ethcore/light/src/provider.rs index 1d9af0ac19b15dd4ebf41a85b1f9d2ce1f5b41e9..84e7b7283942c7c8388d7f4475cd0b76e27c0388 100644 --- a/ethcore/light/src/provider.rs +++ b/ethcore/light/src/provider.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -54,6 +54,7 @@ pub trait Provider: Send + Sync { /// results within must adhere to the `skip` and `reverse` parameters. fn block_headers(&self, req: request::CompleteHeadersRequest) -> Option { use request::HashOrNumber; + const MAX_HEADERS_TO_SEND: u64 = 512; if req.max == 0 { return None } @@ -82,10 +83,12 @@ pub trait Provider: Send + Sync { } }; - let headers: Vec<_> = (0u64..req.max as u64) - .map(|x: u64| x.saturating_mul(req.skip + 1)) + let max = ::std::cmp::min(MAX_HEADERS_TO_SEND, req.max); + + let headers: Vec<_> = (0u64..max) + .map(|x: u64| x.saturating_mul(req.skip.saturating_add(1))) .take_while(|x| if req.reverse { x < &start_num } else { best_num.saturating_sub(start_num) >= *x }) - .map(|x| if req.reverse { start_num - x } else { start_num + x }) + .map(|x| if req.reverse { start_num.saturating_sub(x) } else { start_num.saturating_add(x) }) .map(|x| self.block_header(BlockId::Number(x))) .take_while(|x| x.is_some()) .flat_map(|x| x) @@ -125,7 +128,7 @@ pub trait Provider: Send + Sync { fn header_proof(&self, req: request::CompleteHeaderProofRequest) -> Option; /// Provide pending transactions. - fn ready_transactions(&self) -> Vec; + fn ready_transactions(&self, max_len: usize) -> Vec; /// Provide a proof-of-execution for the given transaction proof request. /// Returns a vector of all state items necessary to execute the transaction. @@ -280,8 +283,11 @@ impl Provider for T { .map(|(_, proof)| ::request::ExecutionResponse { items: proof }) } - fn ready_transactions(&self) -> Vec { - BlockChainClient::ready_transactions(self) + fn ready_transactions(&self, max_len: usize) -> Vec { + BlockChainClient::ready_transactions(self, max_len) + .into_iter() + .map(|tx| tx.pending().clone()) + .collect() } fn epoch_signal(&self, req: request::CompleteSignalRequest) -> Option { @@ -364,9 +370,12 @@ impl Provider for LightProvider { None } - fn ready_transactions(&self) -> Vec { + fn ready_transactions(&self, max_len: usize) -> Vec { let chain_info = self.chain_info(); - self.txqueue.read().ready_transactions(chain_info.best_block_number, chain_info.best_block_timestamp) + let mut transactions = self.txqueue.read() + .ready_transactions(chain_info.best_block_number, chain_info.best_block_timestamp); + transactions.truncate(max_len); + transactions } } diff --git a/ethcore/light/src/transaction_queue.rs b/ethcore/light/src/transaction_queue.rs index 156152253dd6666708c8e3638abf8adc6d25a11d..e8880037a171d0d22b4078293aae6859e4499cad 100644 --- a/ethcore/light/src/transaction_queue.rs +++ b/ethcore/light/src/transaction_queue.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -120,6 +120,18 @@ impl AccountTransactions { } } +/// Transaction import result. +pub enum ImportDestination { + /// Transaction has been imported to the current queue. + /// + /// It's going to be propagated to peers. + Current, + /// Transaction has been imported to future queue. + /// + /// It means it won't be propagated until the gap is filled. + Future, +} + type Listener = Box; /// Light transaction queue. See module docs for more details. @@ -142,7 +154,7 @@ impl fmt::Debug for TransactionQueue { impl TransactionQueue { /// Import a pending transaction to be queued. - pub fn import(&mut self, tx: PendingTransaction) -> Result { + pub fn import(&mut self, tx: PendingTransaction) -> Result { let sender = tx.sender(); let hash = tx.hash(); let nonce = tx.nonce; @@ -158,7 +170,7 @@ impl TransactionQueue { future: BTreeMap::new(), }); - (transaction::ImportResult::Current, vec![hash]) + (ImportDestination::Current, vec![hash]) } Entry::Occupied(mut entry) => { let acct_txs = entry.get_mut(); @@ -180,7 +192,7 @@ impl TransactionQueue { let old = ::std::mem::replace(&mut acct_txs.current[idx], tx_info); self.by_hash.remove(&old.hash); - (transaction::ImportResult::Current, vec![hash]) + (ImportDestination::Current, vec![hash]) } Err(idx) => { let cur_len = acct_txs.current.len(); @@ -202,13 +214,13 @@ impl TransactionQueue { acct_txs.future.insert(future_nonce, future); } - (transaction::ImportResult::Current, vec![hash]) + (ImportDestination::Current, vec![hash]) } else if idx == cur_len && acct_txs.current.last().map_or(false, |f| f.nonce + 1.into() != nonce) { trace!(target: "txqueue", "Queued future transaction for {}, nonce={}", sender, nonce); let future_nonce = nonce; acct_txs.future.insert(future_nonce, tx_info); - (transaction::ImportResult::Future, vec![]) + (ImportDestination::Future, vec![]) } else { trace!(target: "txqueue", "Queued current transaction for {}, nonce={}", sender, nonce); @@ -217,7 +229,7 @@ impl TransactionQueue { let mut promoted = acct_txs.adjust_future(); promoted.insert(0, hash); - (transaction::ImportResult::Current, promoted) + (ImportDestination::Current, promoted) } } } diff --git a/ethcore/light/src/types/mod.rs b/ethcore/light/src/types/mod.rs index eba551b533c396d027c868f893165caba2d8315e..67e54141b27615e34a988c87455be0b289b333c7 100644 --- a/ethcore/light/src/types/mod.rs +++ b/ethcore/light/src/types/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/light/src/types/request/batch.rs b/ethcore/light/src/types/request/batch.rs index 21f1264672dd0b97e30cc15db5da35ea4040387a..16843ae02c5808a9117823ef03bbce788e9abdb7 100644 --- a/ethcore/light/src/types/request/batch.rs +++ b/ethcore/light/src/types/request/batch.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/light/src/types/request/mod.rs b/ethcore/light/src/types/request/mod.rs index bd68c6a0a20dec74f5bda3dd846832252f3272f5..538aa0c6bf4c917d44e5fa7a23dce3d2adaf6c82 100644 --- a/ethcore/light/src/types/request/mod.rs +++ b/ethcore/light/src/types/request/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -16,7 +16,7 @@ //! Light protocol request types. -use rlp::{Encodable, Decodable, DecoderError, RlpStream, UntrustedRlp}; +use rlp::{Encodable, Decodable, DecoderError, RlpStream, Rlp}; use ethereum_types::H256; mod batch; @@ -124,8 +124,6 @@ impl Field { } } - - // attempt conversion into scalar value. fn into_scalar(self) -> Result { match self { @@ -148,7 +146,7 @@ impl From for Field { } impl Decodable for Field { - fn decode(rlp: &UntrustedRlp) -> Result { + fn decode(rlp: &Rlp) -> Result { match rlp.val_at::(0)? { 0 => Ok(Field::Scalar(rlp.val_at::(1)?)), 1 => Ok({ @@ -224,7 +222,7 @@ impl From for HashOrNumber { } impl Decodable for HashOrNumber { - fn decode(rlp: &UntrustedRlp) -> Result { + fn decode(rlp: &Rlp) -> Result { rlp.as_val::().map(HashOrNumber::Hash) .or_else(|_| rlp.as_val().map(HashOrNumber::Number)) } @@ -331,7 +329,7 @@ impl Request { } impl Decodable for Request { - fn decode(rlp: &UntrustedRlp) -> Result { + fn decode(rlp: &Rlp) -> Result { match rlp.val_at::(0)? { Kind::Headers => Ok(Request::Headers(rlp.val_at(1)?)), Kind::HeaderProof => Ok(Request::HeaderProof(rlp.val_at(1)?)), @@ -493,7 +491,7 @@ pub enum Kind { } impl Decodable for Kind { - fn decode(rlp: &UntrustedRlp) -> Result { + fn decode(rlp: &Rlp) -> Result { match rlp.as_val::()? { 0 => Ok(Kind::Headers), 1 => Ok(Kind::HeaderProof), @@ -578,7 +576,7 @@ impl Response { } impl Decodable for Response { - fn decode(rlp: &UntrustedRlp) -> Result { + fn decode(rlp: &Rlp) -> Result { match rlp.val_at::(0)? { Kind::Headers => Ok(Response::Headers(rlp.val_at(1)?)), Kind::HeaderProof => Ok(Response::HeaderProof(rlp.val_at(1)?)), @@ -673,7 +671,7 @@ pub trait ResponseLike { pub mod header { use super::{Field, HashOrNumber, NoSuchOutput, OutputKind, Output}; use ethcore::encoded; - use rlp::{Encodable, Decodable, DecoderError, RlpStream, UntrustedRlp}; + use rlp::{Encodable, Decodable, DecoderError, RlpStream, Rlp}; /// Potentially incomplete headers request. #[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)] @@ -754,7 +752,7 @@ pub mod header { } impl Decodable for Response { - fn decode(rlp: &UntrustedRlp) -> Result { + fn decode(rlp: &Rlp) -> Result { use ethcore::header::Header as FullHeader; let mut headers = Vec::new(); @@ -785,7 +783,7 @@ pub mod header { /// Request and response for header proofs. pub mod header_proof { use super::{Field, NoSuchOutput, OutputKind, Output}; - use rlp::{Encodable, Decodable, DecoderError, RlpStream, UntrustedRlp}; + use rlp::{Encodable, Decodable, DecoderError, RlpStream, Rlp}; use ethereum_types::{H256, U256}; use bytes::Bytes; @@ -859,7 +857,7 @@ pub mod header_proof { } impl Decodable for Response { - fn decode(rlp: &UntrustedRlp) -> Result { + fn decode(rlp: &Rlp) -> Result { Ok(Response { proof: rlp.list_at(0)?, hash: rlp.val_at(1)?, @@ -1027,7 +1025,7 @@ pub mod block_receipts { pub mod block_body { use super::{Field, NoSuchOutput, OutputKind, Output}; use ethcore::encoded; - use rlp::{Encodable, Decodable, DecoderError, RlpStream, UntrustedRlp}; + use rlp::{Encodable, Decodable, DecoderError, RlpStream, Rlp}; use ethereum_types::H256; /// Potentially incomplete block body request. @@ -1092,7 +1090,7 @@ pub mod block_body { } impl Decodable for Response { - fn decode(rlp: &UntrustedRlp) -> Result { + fn decode(rlp: &Rlp) -> Result { use ethcore::header::Header as FullHeader; use transaction::UnverifiedTransaction; @@ -1411,7 +1409,7 @@ pub mod contract_code { pub mod execution { use super::{Field, NoSuchOutput, OutputKind, Output}; use transaction::Action; - use rlp::{Encodable, Decodable, DecoderError, RlpStream, UntrustedRlp}; + use rlp::{Encodable, Decodable, DecoderError, RlpStream, Rlp}; use ethereum_types::{H256, U256, Address}; use kvdb::DBValue; use bytes::Bytes; @@ -1508,7 +1506,7 @@ pub mod execution { } impl Decodable for Response { - fn decode(rlp: &UntrustedRlp) -> Result { + fn decode(rlp: &Rlp) -> Result { let mut items = Vec::new(); for raw_item in rlp.iter() { let mut item = DBValue::new(); @@ -1536,7 +1534,7 @@ pub mod execution { /// A request for epoch signal data. pub mod epoch_signal { use super::{Field, NoSuchOutput, OutputKind, Output}; - use rlp::{Encodable, Decodable, DecoderError, RlpStream, UntrustedRlp}; + use rlp::{Encodable, Decodable, DecoderError, RlpStream, Rlp}; use ethereum_types::H256; use bytes::Bytes; @@ -1548,7 +1546,7 @@ pub mod epoch_signal { } impl Decodable for Incomplete { - fn decode(rlp: &UntrustedRlp) -> Result { + fn decode(rlp: &Rlp) -> Result { Ok(Incomplete { block_hash: rlp.val_at(0)?, }) @@ -1617,7 +1615,7 @@ pub mod epoch_signal { } impl Decodable for Response { - fn decode(rlp: &UntrustedRlp) -> Result { + fn decode(rlp: &Rlp) -> Result { Ok(Response { signal: rlp.as_val()?, @@ -1642,7 +1640,7 @@ mod tests { { // check as single value. let bytes = ::rlp::encode(&val); - let new_val: T = ::rlp::decode(&bytes); + let new_val: T = ::rlp::decode(&bytes).unwrap(); assert_eq!(val, new_val); // check as list containing single value. @@ -1891,7 +1889,7 @@ mod tests { stream.append(&100usize).append_list(&reqs); let out = stream.out(); - let rlp = UntrustedRlp::new(&out); + let rlp = Rlp::new(&out); assert_eq!(rlp.val_at::(0).unwrap(), 100usize); assert_eq!(rlp.list_at::(1).unwrap(), reqs); } diff --git a/ethcore/migrations/Cargo.toml b/ethcore/migrations/Cargo.toml deleted file mode 100644 index 561925be4c5a23f2e0a5d46ebb18fa5b40971e2e..0000000000000000000000000000000000000000 --- a/ethcore/migrations/Cargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "ethcore-migrations" -version = "0.1.0" -authors = ["Parity Technologies "] - -[dependencies] -migration = { path = "../../util/migration" } diff --git a/ethcore/migrations/src/lib.rs b/ethcore/migrations/src/lib.rs deleted file mode 100644 index 429c39102cb305adb184bffe35c7818c3a758ff4..0000000000000000000000000000000000000000 --- a/ethcore/migrations/src/lib.rs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2015-2017 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 . - -//! Database migrations. - -extern crate migration; - -use migration::ChangeColumns; - -/// The migration from v10 to v11. -/// Adds a column for node info. -pub const TO_V11: ChangeColumns = ChangeColumns { - pre_columns: Some(6), - post_columns: Some(7), - version: 11, -}; - -/// The migration from v11 to v12. -/// Adds a column for light chain storage. -pub const TO_V12: ChangeColumns = ChangeColumns { - pre_columns: Some(7), - post_columns: Some(8), - version: 12, -}; diff --git a/ethcore/node_filter/Cargo.toml b/ethcore/node_filter/Cargo.toml index c183853d151c52319884e286fde6bd0b9a646ead..11be807652bbf54de6037a75a7bb87079c50386d 100644 --- a/ethcore/node_filter/Cargo.toml +++ b/ethcore/node_filter/Cargo.toml @@ -3,14 +3,14 @@ description = "Parity smart network connections" homepage = "http://parity.io" license = "GPL-3.0" name = "node-filter" -version = "1.11.0" +version = "1.12.0" authors = ["Parity Technologies "] [dependencies] ethcore = { path = ".."} -ethcore-bytes = { path = "../../util/bytes" } +ethcore-network = { path = "../../util/network" } ethcore-network-devp2p = { path = "../../util/network-devp2p" } -ethereum-types = "0.2" +ethereum-types = "0.3" log = "0.3" parking_lot = "0.5" ethabi = "5.1" @@ -19,6 +19,7 @@ ethabi-contract = "5.0" lru-cache = "0.1" [dev-dependencies] +ethcore = { path = "..", features = ["test-helpers"] } kvdb-memorydb = { path = "../../util/kvdb-memorydb" } ethcore-io = { path = "../../util/io" } tempdir = "0.3" diff --git a/ethcore/node_filter/src/lib.rs b/ethcore/node_filter/src/lib.rs index 945f552faf01faf15dc00a9dfd0513ec28631ddb..76f6fd18ffdf6f7315ee868ca97993990f574147 100644 --- a/ethcore/node_filter/src/lib.rs +++ b/ethcore/node_filter/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -18,8 +18,8 @@ extern crate ethabi; extern crate ethcore; -extern crate ethcore_bytes as bytes; -extern crate ethcore_network_devp2p as network; +extern crate ethcore_network as network; +extern crate ethcore_network_devp2p as devp2p; extern crate ethereum_types; extern crate lru_cache; extern crate parking_lot; @@ -42,10 +42,10 @@ use std::sync::Weak; use lru_cache::LruCache; use parking_lot::Mutex; -use bytes::Bytes; -use ethcore::client::{BlockChainClient, BlockId, ChainNotify}; +use ethcore::client::{BlockChainClient, BlockId}; use ethereum_types::{H256, Address}; -use network::{NodeId, ConnectionFilter, ConnectionDirection}; +use network::{ConnectionFilter, ConnectionDirection}; +use devp2p::NodeId; use_contract!(peer_set, "PeerSet", "res/peer_set.json"); @@ -56,7 +56,7 @@ pub struct NodeFilter { contract: peer_set::PeerSet, client: Weak, contract_address: Address, - permission_cache: Mutex>, + permission_cache: Mutex>, } impl NodeFilter { @@ -64,31 +64,32 @@ impl NodeFilter { pub fn new(client: Weak, contract_address: Address) -> NodeFilter { NodeFilter { contract: peer_set::PeerSet::default(), - client: client, - contract_address: contract_address, + client, + contract_address, permission_cache: Mutex::new(LruCache::new(MAX_CACHE_SIZE)), } } - - /// Clear cached permissions. - pub fn clear_cache(&self) { - self.permission_cache.lock().clear(); - } } impl ConnectionFilter for NodeFilter { fn connection_allowed(&self, own_id: &NodeId, connecting_id: &NodeId, _direction: ConnectionDirection) -> bool { - - let mut cache = self.permission_cache.lock(); - if let Some(res) = cache.get_mut(connecting_id) { - return *res; - } - let client = match self.client.upgrade() { Some(client) => client, None => return false, }; + let block_hash = match client.block_hash(BlockId::Latest) { + Some(block_hash) => block_hash, + None => return false, + }; + + let key = (block_hash, *connecting_id); + + let mut cache = self.permission_cache.lock(); + if let Some(res) = cache.get_mut(&key) { + return *res; + } + let address = self.contract_address; let own_low = H256::from_slice(&own_id[0..32]); let own_high = H256::from_slice(&own_id[32..64]); @@ -103,28 +104,17 @@ impl ConnectionFilter for NodeFilter { false }); - cache.insert(*connecting_id, allowed); + cache.insert(key, allowed); allowed } } -impl ChainNotify for NodeFilter { - fn new_blocks(&self, imported: Vec, _invalid: Vec, _enacted: Vec, _retracted: Vec, _sealed: Vec, _proposed: Vec, _duration: u64) { - if !imported.is_empty() { - self.clear_cache(); - } - } -} - - #[cfg(test)] mod test { use std::sync::{Arc, Weak}; - use std::str::FromStr; use ethcore::spec::Spec; use ethcore::client::{BlockChainClient, Client, ClientConfig}; use ethcore::miner::Miner; - use ethereum_types::Address; use network::{ConnectionDirection, ConnectionFilter, NodeId}; use io::IoChannel; use super::NodeFilter; @@ -133,7 +123,7 @@ mod test { /// Contract code: https://gist.github.com/arkpar/467dbcc73cbb85b0997a7a10ffa0695f #[test] fn node_filter() { - let contract_addr = Address::from_str("0000000000000000000000000000000000000005").unwrap(); + let contract_addr = "0000000000000000000000000000000000000005".into(); let data = include_bytes!("../res/node_filter.json"); let tempdir = TempDir::new("").unwrap(); let spec = Spec::load(&tempdir.path(), &data[..]).unwrap(); @@ -143,21 +133,19 @@ mod test { ClientConfig::default(), &spec, client_db, - Arc::new(Miner::with_spec(&spec)), + Arc::new(Miner::new_for_tests(&spec, None)), IoChannel::disconnected(), ).unwrap(); let filter = NodeFilter::new(Arc::downgrade(&client) as Weak, contract_addr); - let self1 = NodeId::from_str("00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002").unwrap(); - let self2 = NodeId::from_str("00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003").unwrap(); - let node1 = NodeId::from_str("00000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000012").unwrap(); - let node2 = NodeId::from_str("00000000000000000000000000000000000000000000000000000000000000210000000000000000000000000000000000000000000000000000000000000022").unwrap(); - let nodex = NodeId::from_str("77000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap(); + let self1: NodeId = "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002".into(); + let self2: NodeId = "00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003".into(); + let node1: NodeId = "00000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000012".into(); + let node2: NodeId = "00000000000000000000000000000000000000000000000000000000000000210000000000000000000000000000000000000000000000000000000000000022".into(); + let nodex: NodeId = "77000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".into(); assert!(filter.connection_allowed(&self1, &node1, ConnectionDirection::Inbound)); assert!(filter.connection_allowed(&self1, &nodex, ConnectionDirection::Inbound)); - filter.clear_cache(); assert!(filter.connection_allowed(&self2, &node1, ConnectionDirection::Inbound)); assert!(filter.connection_allowed(&self2, &node2, ConnectionDirection::Inbound)); - assert!(!filter.connection_allowed(&self2, &nodex, ConnectionDirection::Inbound)); } } diff --git a/ethcore/private-tx/Cargo.toml b/ethcore/private-tx/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..441c9882e29233820025b7f039ad4a3d40791f10 --- /dev/null +++ b/ethcore/private-tx/Cargo.toml @@ -0,0 +1,40 @@ +[package] +description = "Parity Private Transactions" +name = "ethcore-private-tx" +version = "1.0.0" +license = "GPL-3.0" +authors = ["Parity Technologies "] + +[dependencies] +error-chain = { version = "0.11", default-features = false } +ethabi = "5.1" +ethabi-contract = "5.0" +ethabi-derive = "5.0" +ethcore = { path = ".." } +ethcore-bytes = { path = "../../util/bytes" } +ethcore-crypto = { path = "../crypto" } +ethcore-io = { path = "../../util/io" } +ethcore-logger = { path = "../../logger" } +ethcore-miner = { path = "../../miner" } +ethcore-transaction = { path = "../transaction" } +ethereum-types = "0.3" +ethjson = { path = "../../json" } +ethkey = { path = "../../ethkey" } +fetch = { path = "../../util/fetch" } +futures = "0.1" +keccak-hash = { path = "../../util/hash" } +log = "0.3" +parking_lot = "0.5" +patricia-trie = { path = "../../util/patricia_trie" } +rand = "0.3" +rlp = { path = "../../util/rlp" } +rlp_derive = { path = "../../util/rlp_derive" } +rustc-hex = "1.0" +serde = "1.0" +serde_derive = "1.0" +serde_json = "1.0" +tiny-keccak = "1.4" +url = "1" + +[dev-dependencies] +ethcore = { path = "..", features = ["test-helpers"] } diff --git a/ethcore/private-tx/res/private.evm b/ethcore/private-tx/res/private.evm new file mode 100644 index 0000000000000000000000000000000000000000..cd19d757aadeed88450d1cca61dba2f6a21e101a --- /dev/null +++ b/ethcore/private-tx/res/private.evm @@ -0,0 +1 @@ +6060604052341561000f57600080fd5b604051610b0d380380610b0d833981016040528080518201919060200180518201919060200180518201919050508260009080519060200190610053929190610092565b50816002908051906020019061006a92919061011c565b50806001908051906020019061008192919061011c565b506001600381905550505050610204565b82805482825590600052602060002090810192821561010b579160200282015b8281111561010a5782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550916020019190600101906100b2565b5b509050610118919061019c565b5090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061015d57805160ff191683800117855561018b565b8280016001018555821561018b579182015b8281111561018a57825182559160200191906001019061016f565b5b50905061019891906101df565b5090565b6101dc91905b808211156101d857600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055506001016101a2565b5090565b90565b61020191905b808211156101fd5760008160009055506001016101e5565b5090565b90565b6108fa806102136000396000f300606060405260043610610078576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806317ac53a21461007d57806324c12bf61461019a57806335aa2e4414610228578063affed0e01461028b578063b7ab4db5146102b4578063c19d93fb1461031e575b600080fd5b341561008857600080fd5b610198600480803590602001908201803590602001908080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050919080359060200190820180359060200190808060200260200160405190810160405280939291908181526020018383602002808284378201915050505050509190803590602001908201803590602001908080602002602001604051908101604052809392919081815260200183836020028082843782019150505050505091908035906020019082018035906020019080806020026020016040519081016040528093929190818152602001838360200280828437820191505050505050919050506103ac565b005b34156101a557600080fd5b6101ad610600565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101ed5780820151818401526020810190506101d2565b50505050905090810190601f16801561021a5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561023357600080fd5b610249600480803590602001909190505061069e565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561029657600080fd5b61029e6106dd565b6040518082815260200191505060405180910390f35b34156102bf57600080fd5b6102c76106e3565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561030a5780820151818401526020810190506102ef565b505050509050019250505060405180910390f35b341561032957600080fd5b610331610777565b6040518080602001828103825283818151815260200191508051906020019080838360005b83811015610371578082015181840152602081019050610356565b50505050905090810190601f16801561039e5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6000806040805190810160405280876040518082805190602001908083835b6020831015156103f057805182526020820191506020810190506020830392506103cb565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390206000191660001916815260200160035460010260001916600019168152506040518082600260200280838360005b8381101561046657808201518184015260208101905061044b565b5050505090500191505060405180910390209150600090505b6000805490508110156105d55760008181548110151561049b57fe5b906000526020600020900160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660018387848151811015156104ee57fe5b90602001906020020151878581518110151561050657fe5b90602001906020020151878681518110151561051e57fe5b90602001906020020151604051600081526020016040526000604051602001526040518085600019166000191681526020018460ff1660ff16815260200183600019166000191681526020018260001916600019168152602001945050505050602060405160208103908084039060008661646e5a03f115156105a057600080fd5b50506020604051035173ffffffffffffffffffffffffffffffffffffffff161415156105c857fe5b808060010191505061047f565b85600190805190602001906105eb929190610815565b50600160035401600381905550505050505050565b60028054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156106965780601f1061066b57610100808354040283529160200191610696565b820191906000526020600020905b81548152906001019060200180831161067957829003601f168201915b505050505081565b6000818154811015156106ad57fe5b90600052602060002090016000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60035481565b6106eb610895565b600080548060200260200160405190810160405280929190818152602001828054801561076d57602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311610723575b5050505050905090565b60018054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561080d5780601f106107e25761010080835404028352916020019161080d565b820191906000526020600020905b8154815290600101906020018083116107f057829003601f168201915b505050505081565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061085657805160ff1916838001178555610884565b82800160010185558215610884579182015b82811115610883578251825591602001919060010190610868565b5b50905061089191906108a9565b5090565b602060405190810160405280600081525090565b6108cb91905b808211156108c75760008160009055506001016108af565b5090565b905600a165627a7a723058200ae0215fae320b646a22fdd58278b328f46d915bd65ddbfeb5b4a09643d6e0220029 diff --git a/ethcore/private-tx/res/private.json b/ethcore/private-tx/res/private.json new file mode 100644 index 0000000000000000000000000000000000000000..82a5e86bc2ebf3e9eac4c133061b3d908bee4fac --- /dev/null +++ b/ethcore/private-tx/res/private.json @@ -0,0 +1 @@ +[{"constant": false,"inputs": [{"name": "newState","type": "bytes"},{"name": "v","type": "uint8[]"},{"name": "r","type": "bytes32[]"},{"name": "s","type": "bytes32[]"}],"name": "setState","outputs": [],"payable": false,"stateMutability": "nonpayable","type": "function"},{"constant": true,"inputs": [],"name": "code","outputs": [{"name": "","type": "bytes"}],"payable": false,"stateMutability": "view","type": "function"},{"constant": true,"inputs": [{"name": "","type": "uint256"}],"name": "validators","outputs": [{"name": "","type": "address"}],"payable": false,"stateMutability": "view","type": "function"},{"constant": true,"inputs": [],"name": "nonce","outputs": [{"name": "","type": "uint256"}],"payable": false,"stateMutability": "view","type": "function"},{"constant": true,"inputs": [],"name": "getValidators","outputs": [{"name": "","type": "address[]"}],"payable": false,"stateMutability": "view","type": "function"},{"constant": true,"inputs": [],"name": "state","outputs": [{"name": "","type": "bytes"}],"payable": false,"stateMutability": "view","type": "function"},{"inputs": [{"name": "initialValidators","type": "address[]"},{"name": "initialCode","type": "bytes"},{"name": "initialState","type": "bytes"}],"payable": false,"stateMutability": "nonpayable","type": "constructor"}] diff --git a/ethcore/private-tx/src/encryptor.rs b/ethcore/private-tx/src/encryptor.rs new file mode 100644 index 0000000000000000000000000000000000000000..e171e3e606e9dfb76138d99713bdd7844e1caf30 --- /dev/null +++ b/ethcore/private-tx/src/encryptor.rs @@ -0,0 +1,276 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Encryption providers. + +use std::io::Read; +use std::str::FromStr; +use std::iter::repeat; +use std::time::{Instant, Duration}; +use std::collections::HashMap; +use std::collections::hash_map::Entry; +use parking_lot::Mutex; +use ethcore::account_provider::AccountProvider; +use ethereum_types::{H128, H256, Address}; +use ethjson; +use ethkey::{Signature, Public}; +use crypto; +use futures::Future; +use fetch::{Fetch, Client as FetchClient, Method, BodyReader, Request}; +use bytes::{Bytes, ToPretty}; +use error::{Error, ErrorKind}; +use url::Url; +use super::find_account_password; + +/// Initialization vector length. +const INIT_VEC_LEN: usize = 16; + +/// Duration of storing retrieved keys (in ms) +const ENCRYPTION_SESSION_DURATION: u64 = 30 * 1000; + +/// Trait for encryption/decryption operations. +pub trait Encryptor: Send + Sync + 'static { + /// Generate unique contract key && encrypt passed data. Encryption can only be performed once. + fn encrypt( + &self, + contract_address: &Address, + accounts: &AccountProvider, + initialisation_vector: &H128, + plain_data: &[u8], + ) -> Result; + + /// Decrypt data using previously generated contract key. + fn decrypt( + &self, + contract_address: &Address, + accounts: &AccountProvider, + cypher: &[u8], + ) -> Result; +} + +/// Configurtion for key server encryptor +#[derive(Default, PartialEq, Debug, Clone)] +pub struct EncryptorConfig { + /// URL to key server + pub base_url: Option, + /// Key server's threshold + pub threshold: u32, + /// Account used for signing requests to key server + pub key_server_account: Option
, + /// Passwords used to unlock accounts + pub passwords: Vec, +} + +struct EncryptionSession { + key: Bytes, + end_time: Instant, +} + +/// SecretStore-based encryption/decryption operations. +pub struct SecretStoreEncryptor { + config: EncryptorConfig, + client: FetchClient, + sessions: Mutex>, +} + +impl SecretStoreEncryptor { + /// Create new encryptor + pub fn new(config: EncryptorConfig, client: FetchClient) -> Result { + Ok(SecretStoreEncryptor { + config, + client, + sessions: Mutex::default(), + }) + } + + /// Ask secret store for key && decrypt the key. + fn retrieve_key( + &self, + url_suffix: &str, + use_post: bool, + contract_address: &Address, + accounts: &AccountProvider, + ) -> Result { + // check if the key was already cached + if let Some(key) = self.obtained_key(contract_address) { + return Ok(key); + } + let contract_address_signature = self.sign_contract_address(contract_address, accounts)?; + let requester = self.config.key_server_account.ok_or_else(|| ErrorKind::KeyServerAccountNotSet)?; + + // key id in SS is H256 && we have H160 here => expand with assitional zeros + let contract_address_extended: H256 = contract_address.into(); + let base_url = self.config.base_url.clone().ok_or_else(|| ErrorKind::KeyServerNotSet)?; + + // prepare request url + let url = format!("{}/{}/{}{}", + base_url, + contract_address_extended.to_hex(), + contract_address_signature, + url_suffix, + ); + + // send HTTP request + let method = if use_post { + Method::Post + } else { + Method::Get + }; + + let url = Url::from_str(&url).map_err(|e| ErrorKind::Encrypt(e.to_string()))?; + let response = self.client.fetch(Request::new(url, method), Default::default()).wait() + .map_err(|e| ErrorKind::Encrypt(e.to_string()))?; + + if response.is_not_found() { + bail!(ErrorKind::EncryptionKeyNotFound(*contract_address)); + } + + if !response.is_success() { + bail!(ErrorKind::Encrypt(response.status().canonical_reason().unwrap_or("unknown").into())); + } + + // read HTTP response + let mut result = String::new(); + BodyReader::new(response).read_to_string(&mut result)?; + + // response is JSON string (which is, in turn, hex-encoded, encrypted Public) + let encrypted_bytes: ethjson::bytes::Bytes = result.trim_matches('\"').parse().map_err(|e| ErrorKind::Encrypt(e))?; + let password = find_account_password(&self.config.passwords, &*accounts, &requester); + + // decrypt Public + let decrypted_bytes = accounts.decrypt(requester, password, &crypto::DEFAULT_MAC, &encrypted_bytes)?; + let decrypted_key = Public::from_slice(&decrypted_bytes); + + // and now take x coordinate of Public as a key + let key: Bytes = (*decrypted_key)[..INIT_VEC_LEN].into(); + + // cache the key in the session and clear expired sessions + self.sessions.lock().insert(*contract_address, EncryptionSession{ + key: key.clone(), + end_time: Instant::now() + Duration::from_millis(ENCRYPTION_SESSION_DURATION), + }); + self.clean_expired_sessions(); + Ok(key) + } + + fn clean_expired_sessions(&self) { + let mut sessions = self.sessions.lock(); + sessions.retain(|_, session| session.end_time < Instant::now()); + } + + fn obtained_key(&self, contract_address: &Address) -> Option { + let mut sessions = self.sessions.lock(); + let stored_session = sessions.entry(*contract_address); + match stored_session { + Entry::Occupied(session) => { + if Instant::now() > session.get().end_time { + session.remove_entry(); + None + } else { + Some(session.get().key.clone()) + } + } + Entry::Vacant(_) => None, + } + } + + fn sign_contract_address(&self, contract_address: &Address, accounts: &AccountProvider) -> Result { + // key id in SS is H256 && we have H160 here => expand with assitional zeros + let contract_address_extended: H256 = contract_address.into(); + let key_server_account = self.config.key_server_account.ok_or_else(|| ErrorKind::KeyServerAccountNotSet)?; + let password = find_account_password(&self.config.passwords, accounts, &key_server_account); + Ok(accounts.sign(key_server_account, password, H256::from_slice(&contract_address_extended))?) + } +} + +impl Encryptor for SecretStoreEncryptor { + fn encrypt( + &self, + contract_address: &Address, + accounts: &AccountProvider, + initialisation_vector: &H128, + plain_data: &[u8], + ) -> Result { + // retrieve the key, try to generate it if it doesn't exist yet + let key = match self.retrieve_key("", false, contract_address, &*accounts) { + Ok(key) => Ok(key), + Err(Error(ErrorKind::EncryptionKeyNotFound(_), _)) => { + trace!("Key for account wasnt found in sstore. Creating. Address: {:?}", contract_address); + self.retrieve_key(&format!("/{}", self.config.threshold), true, contract_address, &*accounts) + } + Err(err) => Err(err), + }?; + + // encrypt data + let mut cypher = Vec::with_capacity(plain_data.len() + initialisation_vector.len()); + cypher.extend(repeat(0).take(plain_data.len())); + crypto::aes::encrypt_128_ctr(&key, initialisation_vector, plain_data, &mut cypher) + .map_err(|e| ErrorKind::Encrypt(e.to_string()))?; + cypher.extend_from_slice(&initialisation_vector); + + Ok(cypher) + } + + /// Decrypt data using previously generated contract key. + fn decrypt( + &self, + contract_address: &Address, + accounts: &AccountProvider, + cypher: &[u8], + ) -> Result { + // initialization vector takes INIT_VEC_LEN bytes + let cypher_len = cypher.len(); + if cypher_len < INIT_VEC_LEN { + bail!(ErrorKind::Decrypt("Invalid cypher".into())); + } + + // retrieve existing key + let key = self.retrieve_key("", false, contract_address, accounts)?; + + // use symmetric decryption to decrypt document + let (cypher, iv) = cypher.split_at(cypher_len - INIT_VEC_LEN); + let mut plain_data = Vec::with_capacity(cypher_len - INIT_VEC_LEN); + plain_data.extend(repeat(0).take(cypher_len - INIT_VEC_LEN)); + crypto::aes::decrypt_128_ctr(&key, &iv, cypher, &mut plain_data) + .map_err(|e| ErrorKind::Decrypt(e.to_string()))?; + Ok(plain_data) + } +} + +/// Dummy encryptor. +#[derive(Default)] +pub struct NoopEncryptor; + +impl Encryptor for NoopEncryptor { + fn encrypt( + &self, + _contract_address: &Address, + _accounts: &AccountProvider, + _initialisation_vector: &H128, + data: &[u8], + ) -> Result { + Ok(data.to_vec()) + } + + fn decrypt( + &self, + _contract_address: &Address, + _accounts: &AccountProvider, + data: &[u8], + ) -> Result { + Ok(data.to_vec()) + } +} diff --git a/ethcore/private-tx/src/error.rs b/ethcore/private-tx/src/error.rs new file mode 100644 index 0000000000000000000000000000000000000000..0456b33053018d83d1ad3dfc93af3dd0367fbd27 --- /dev/null +++ b/ethcore/private-tx/src/error.rs @@ -0,0 +1,207 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use ethereum_types::Address; +use rlp::DecoderError; +use trie::TrieError; +use ethcore::account_provider::SignError; +use ethcore::error::{Error as EthcoreError, ExecutionError}; +use transaction::Error as TransactionError; +use ethkey::Error as KeyError; + +error_chain! { + foreign_links { + Io(::std::io::Error) #[doc = "Error concerning the Rust standard library's IO subsystem."]; + Decoder(DecoderError) #[doc = "RLP decoding error."]; + Trie(TrieError) #[doc = "Error concerning TrieDBs."]; + } + + errors { + #[doc = "Encryption error."] + Encrypt(err: String) { + description("Encryption error"), + display("Encryption error. ({})", err), + } + + #[doc = "Decryption error."] + Decrypt(err: String) { + description("Decryption error"), + display("Decryption error. ({})", err), + } + + #[doc = "Address not authorized."] + NotAuthorised(address: Address) { + description("Address not authorized"), + display("Private transaction execution is not authorised for {}", address), + } + + #[doc = "Transaction creates more than one contract."] + TooManyContracts { + description("Transaction creates more than one contract."), + display("Private transaction created too many contracts"), + } + + #[doc = "Contract call error."] + Call(err: String) { + description("Contract call error."), + display("Contract call error. ({})", err), + } + + #[doc = "State is not available."] + StatePruned { + description("State is not available."), + display("State is not available"), + } + + #[doc = "State is incorrect."] + StateIncorrect { + description("State is incorrect."), + display("State is incorrect"), + } + + #[doc = "Wrong private transaction type."] + BadTransactonType { + description("Wrong private transaction type."), + display("Wrong private transaction type"), + } + + #[doc = "Contract does not exist or was not created."] + ContractDoesNotExist { + description("Contract does not exist or was not created."), + display("Contract does not exist or was not created"), + } + + #[doc = "Reference to the client is corrupted."] + ClientIsMalformed { + description("Reference to the client is corrupted."), + display("Reference to the client is corrupted"), + } + + #[doc = "Queue of private transactions for verification is full."] + QueueIsFull { + description("Queue of private transactions for verification is full."), + display("Queue of private transactions for verification is full"), + } + + #[doc = "The transaction already exists in queue of private transactions."] + PrivateTransactionAlreadyImported { + description("The transaction already exists in queue of private transactions."), + display("The transaction already exists in queue of private transactions."), + } + + #[doc = "The information about private transaction is not found in the store."] + PrivateTransactionNotFound { + description("The information about private transaction is not found in the store."), + display("The information about private transaction is not found in the store."), + } + + #[doc = "Account for signing public transactions not set."] + SignerAccountNotSet { + description("Account for signing public transactions not set."), + display("Account for signing public transactions not set."), + } + + #[doc = "Account for validating private transactions not set."] + ValidatorAccountNotSet { + description("Account for validating private transactions not set."), + display("Account for validating private transactions not set."), + } + + #[doc = "Account for signing requests to key server not set."] + KeyServerAccountNotSet { + description("Account for signing requests to key server not set."), + display("Account for signing requests to key server not set."), + } + + #[doc = "Encryption key is not found on key server."] + EncryptionKeyNotFound(address: Address) { + description("Encryption key is not found on key server"), + display("Encryption key is not found on key server for {}", address), + } + + #[doc = "Key server URL is not set."] + KeyServerNotSet { + description("Key server URL is not set."), + display("Key server URL is not set."), + } + + #[doc = "VM execution error."] + Execution(err: ExecutionError) { + description("VM execution error."), + display("VM execution error {}", err), + } + + #[doc = "General signing error."] + Key(err: KeyError) { + description("General signing error."), + display("General signing error {}", err), + } + + #[doc = "Account provider signing error."] + Sign(err: SignError) { + description("Account provider signing error."), + display("Account provider signing error {}", err), + } + + #[doc = "Error of transactions processing."] + Transaction(err: TransactionError) { + description("Error of transactions processing."), + display("Error of transactions processing {}", err), + } + + #[doc = "General ethcore error."] + Ethcore(err: EthcoreError) { + description("General ethcore error."), + display("General ethcore error {}", err), + } + } +} + +impl From for Error { + fn from(err: SignError) -> Self { + ErrorKind::Sign(err).into() + } +} + +impl From for Error { + fn from(err: KeyError) -> Self { + ErrorKind::Key(err).into() + } +} + +impl From for Error { + fn from(err: ExecutionError) -> Self { + ErrorKind::Execution(err).into() + } +} + +impl From for Error { + fn from(err: TransactionError) -> Self { + ErrorKind::Transaction(err).into() + } +} + +impl From for Error { + fn from(err: EthcoreError) -> Self { + ErrorKind::Ethcore(err).into() + } +} + +impl From> for Error where Error: From { + fn from(err: Box) -> Error { + Error::from(*err) + } +} diff --git a/ethcore/private-tx/src/lib.rs b/ethcore/private-tx/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..31abdb1eca5079c3a4a363f6c3d4afdb9d1405a4 --- /dev/null +++ b/ethcore/private-tx/src/lib.rs @@ -0,0 +1,691 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Private transactions module. + +// Recursion limit required because of +// error_chain foreign_links. +#![recursion_limit="256"] + +mod encryptor; +mod private_transactions; +mod messages; +mod error; + +extern crate ethcore; +extern crate ethcore_bytes as bytes; +extern crate ethcore_crypto as crypto; +extern crate ethcore_io as io; +extern crate ethcore_miner; +extern crate ethcore_transaction as transaction; +extern crate ethabi; +extern crate ethereum_types; +extern crate ethkey; +extern crate ethjson; +extern crate fetch; +extern crate futures; +extern crate keccak_hash as hash; +extern crate parking_lot; +extern crate patricia_trie as trie; +extern crate rlp; +extern crate url; +extern crate rustc_hex; +#[macro_use] +extern crate log; +#[macro_use] +extern crate ethabi_derive; +#[macro_use] +extern crate ethabi_contract; +#[macro_use] +extern crate error_chain; +#[macro_use] +extern crate rlp_derive; + +#[cfg(test)] +extern crate rand; +#[cfg(test)] +extern crate ethcore_logger; + +pub use encryptor::{Encryptor, SecretStoreEncryptor, EncryptorConfig, NoopEncryptor}; +pub use private_transactions::{PrivateTransactionDesc, VerificationStore, PrivateTransactionSigningDesc, SigningStore}; +pub use messages::{PrivateTransaction, SignedPrivateTransaction}; +pub use error::{Error, ErrorKind}; + +use std::sync::{Arc, Weak}; +use std::collections::{HashMap, HashSet}; +use std::time::Duration; +use ethereum_types::{H128, H256, U256, Address}; +use hash::keccak; +use rlp::*; +use parking_lot::{Mutex, RwLock}; +use bytes::Bytes; +use ethkey::{Signature, recover, public_to_address}; +use io::IoChannel; +use ethcore::executive::{Executive, TransactOptions}; +use ethcore::executed::{Executed}; +use transaction::{SignedTransaction, Transaction, Action, UnverifiedTransaction}; +use ethcore::{contract_address as ethcore_contract_address}; +use ethcore::client::{ + Client, ChainNotify, ChainRoute, ChainMessageType, ClientIoMessage, BlockId, CallContract +}; +use ethcore::account_provider::AccountProvider; +use ethcore::miner::{self, Miner, MinerService}; +use ethcore::trace::{Tracer, VMTracer}; +use rustc_hex::FromHex; + +// Source avaiable at https://github.com/parity-contracts/private-tx/blob/master/contracts/PrivateContract.sol +const DEFAULT_STUB_CONTRACT: &'static str = include_str!("../res/private.evm"); + +use_contract!(private, "PrivateContract", "res/private.json"); + +/// Initialization vector length. +const INIT_VEC_LEN: usize = 16; + +/// Configurtion for private transaction provider +#[derive(Default, PartialEq, Debug, Clone)] +pub struct ProviderConfig { + /// Accounts that can be used for validation + pub validator_accounts: Vec
, + /// Account used for signing public transactions created from private transactions + pub signer_account: Option
, + /// Passwords used to unlock accounts + pub passwords: Vec, +} + +#[derive(Debug)] +/// Private transaction execution receipt. +pub struct Receipt { + /// Private transaction hash. + pub hash: H256, + /// Created contract address if any. + pub contract_address: Option
, + /// Execution status. + pub status_code: u8, +} + +/// Manager of private transactions +pub struct Provider { + encryptor: Box, + validator_accounts: HashSet
, + signer_account: Option
, + passwords: Vec, + notify: RwLock>>, + transactions_for_signing: Mutex, + // TODO [ToDr] Move the Mutex/RwLock inside `VerificationStore` after refactored to `drain`. + transactions_for_verification: Mutex, + client: Arc, + miner: Arc, + accounts: Arc, + channel: IoChannel, +} + +#[derive(Debug)] +pub struct PrivateExecutionResult where T: Tracer, V: VMTracer { + code: Option, + state: Bytes, + contract_address: Option
, + result: Executed, +} + +impl Provider where { + /// Create a new provider. + pub fn new( + client: Arc, + miner: Arc, + accounts: Arc, + encryptor: Box, + config: ProviderConfig, + channel: IoChannel, + ) -> Self { + Provider { + encryptor, + validator_accounts: config.validator_accounts.into_iter().collect(), + signer_account: config.signer_account, + passwords: config.passwords, + notify: RwLock::default(), + transactions_for_signing: Mutex::default(), + transactions_for_verification: Mutex::default(), + client, + miner, + accounts, + channel, + } + } + + // TODO [ToDr] Don't use `ChainNotify` here! + // Better to create a separate notification type for this. + /// Adds an actor to be notified on certain events + pub fn add_notify(&self, target: Arc) { + self.notify.write().push(Arc::downgrade(&target)); + } + + fn notify(&self, f: F) where F: Fn(&ChainNotify) { + for np in self.notify.read().iter() { + if let Some(n) = np.upgrade() { + f(&*n); + } + } + } + + /// 1. Create private transaction from the signed transaction + /// 2. Executes private transaction + /// 3. Save it with state returned on prev step to the queue for signing + /// 4. Broadcast corresponding message to the chain + pub fn create_private_transaction(&self, signed_transaction: SignedTransaction) -> Result { + trace!("Creating private transaction from regular transaction: {:?}", signed_transaction); + if self.signer_account.is_none() { + trace!("Signing account not set"); + bail!(ErrorKind::SignerAccountNotSet); + } + let tx_hash = signed_transaction.hash(); + match signed_transaction.action { + Action::Create => { + bail!(ErrorKind::BadTransactonType); + } + Action::Call(contract) => { + let data = signed_transaction.rlp_bytes(); + let encrypted_transaction = self.encrypt(&contract, &Self::iv_from_transaction(&signed_transaction), &data)?; + let private = PrivateTransaction { + encrypted: encrypted_transaction, + contract, + }; + // TODO [ToDr] Using BlockId::Latest is bad here, + // the block may change in the middle of execution + // causing really weird stuff to happen. + // We should retrieve hash and stick to that. IMHO + // best would be to change the API and only allow H256 instead of BlockID + // in private-tx to avoid such mistakes. + let contract_nonce = self.get_contract_nonce(&contract, BlockId::Latest)?; + let private_state = self.execute_private_transaction(BlockId::Latest, &signed_transaction)?; + trace!("Private transaction created, encrypted transaction: {:?}, private state: {:?}", private, private_state); + let contract_validators = self.get_validators(BlockId::Latest, &contract)?; + trace!("Required validators: {:?}", contract_validators); + let private_state_hash = self.calculate_state_hash(&private_state, contract_nonce); + trace!("Hashed effective private state for sender: {:?}", private_state_hash); + self.transactions_for_signing.lock().add_transaction(private.hash(), signed_transaction, contract_validators, private_state, contract_nonce)?; + self.broadcast_private_transaction(private.rlp_bytes().into_vec()); + Ok(Receipt { + hash: tx_hash, + contract_address: None, + status_code: 0, + }) + } + } + } + + /// Calculate hash from united private state and contract nonce + pub fn calculate_state_hash(&self, state: &Bytes, nonce: U256) -> H256 { + let state_hash = keccak(state); + let mut state_buf = [0u8; 64]; + state_buf[..32].clone_from_slice(&state_hash); + state_buf[32..].clone_from_slice(&H256::from(nonce)); + keccak(&state_buf.as_ref()) + } + + /// Extract signed transaction from private transaction + fn extract_original_transaction(&self, private: PrivateTransaction, contract: &Address) -> Result { + let encrypted_transaction = private.encrypted; + let transaction_bytes = self.decrypt(contract, &encrypted_transaction)?; + let original_transaction: UnverifiedTransaction = Rlp::new(&transaction_bytes).as_val()?; + Ok(original_transaction) + } + + fn pool_client<'a>(&'a self, nonce_cache: &'a RwLock>) -> miner::pool_client::PoolClient<'a, Client> { + let engine = self.client.engine(); + let refuse_service_transactions = true; + miner::pool_client::PoolClient::new( + &*self.client, + nonce_cache, + engine, + Some(&*self.accounts), + refuse_service_transactions, + ) + } + + /// Retrieve and verify the first available private transaction for every sender + /// + /// TODO [ToDr] It seems that: + /// The 3 methods `ready_transaction,get_descriptor,remove` are always used in conjuction so most likely + /// can be replaced with a single `drain()` method instead. + /// Thanks to this we also don't really need to lock the entire verification for the time of execution. + fn process_queue(&self) -> Result<(), Error> { + let nonce_cache = Default::default(); + let mut verification_queue = self.transactions_for_verification.lock(); + let ready_transactions = verification_queue.ready_transactions(self.pool_client(&nonce_cache)); + for transaction in ready_transactions { + let transaction_hash = transaction.signed().hash(); + match verification_queue.private_transaction_descriptor(&transaction_hash) { + Ok(desc) => { + if !self.validator_accounts.contains(&desc.validator_account) { + trace!("Cannot find validator account in config"); + bail!(ErrorKind::ValidatorAccountNotSet); + } + let account = desc.validator_account; + if let Action::Call(contract) = transaction.signed().action { + // TODO [ToDr] Usage of BlockId::Latest + let contract_nonce = self.get_contract_nonce(&contract, BlockId::Latest)?; + let private_state = self.execute_private_transaction(BlockId::Latest, transaction.signed())?; + let private_state_hash = self.calculate_state_hash(&private_state, contract_nonce); + trace!("Hashed effective private state for validator: {:?}", private_state_hash); + let password = find_account_password(&self.passwords, &*self.accounts, &account); + let signed_state = self.accounts.sign(account, password, private_state_hash)?; + let signed_private_transaction = SignedPrivateTransaction::new(desc.private_hash, signed_state, None); + trace!("Sending signature for private transaction: {:?}", signed_private_transaction); + self.broadcast_signed_private_transaction(signed_private_transaction.rlp_bytes().into_vec()); + } else { + warn!("Incorrect type of action for the transaction"); + } + }, + Err(e) => { + warn!("Cannot retrieve descriptor for transaction with error {:?}", e); + } + } + verification_queue.remove_private_transaction(&transaction_hash); + } + Ok(()) + } + + fn last_required_signature(&self, desc: &PrivateTransactionSigningDesc, sign: Signature) -> Result { + if desc.received_signatures.contains(&sign) { + return Ok(false); + } + let state_hash = self.calculate_state_hash(&desc.state, desc.contract_nonce); + match recover(&sign, &state_hash) { + Ok(public) => { + let sender = public_to_address(&public); + match desc.validators.contains(&sender) { + true => { + Ok(desc.received_signatures.len() + 1 == desc.validators.len()) + } + false => { + trace!("Sender's state doesn't correspond to validator's"); + bail!(ErrorKind::StateIncorrect); + } + } + } + Err(err) => { + trace!("Sender's state doesn't correspond to validator's, error {:?}", err); + bail!(err); + } + } + } + + /// Broadcast the private transaction message to the chain + fn broadcast_private_transaction(&self, message: Bytes) { + self.notify(|notify| notify.broadcast(ChainMessageType::PrivateTransaction(message.clone()))); + } + + /// Broadcast signed private transaction message to the chain + fn broadcast_signed_private_transaction(&self, message: Bytes) { + self.notify(|notify| notify.broadcast(ChainMessageType::SignedPrivateTransaction(message.clone()))); + } + + fn iv_from_transaction(transaction: &SignedTransaction) -> H128 { + let nonce = keccak(&transaction.nonce.rlp_bytes()); + let (iv, _) = nonce.split_at(INIT_VEC_LEN); + H128::from_slice(iv) + } + + fn iv_from_address(contract_address: &Address) -> H128 { + let address = keccak(&contract_address.rlp_bytes()); + let (iv, _) = address.split_at(INIT_VEC_LEN); + H128::from_slice(iv) + } + + fn encrypt(&self, contract_address: &Address, initialisation_vector: &H128, data: &[u8]) -> Result { + trace!("Encrypt data using key(address): {:?}", contract_address); + Ok(self.encryptor.encrypt(contract_address, &*self.accounts, initialisation_vector, data)?) + } + + fn decrypt(&self, contract_address: &Address, data: &[u8]) -> Result { + trace!("Decrypt data using key(address): {:?}", contract_address); + Ok(self.encryptor.decrypt(contract_address, &*self.accounts, data)?) + } + + fn get_decrypted_state(&self, address: &Address, block: BlockId) -> Result { + let contract = private::PrivateContract::default(); + let state = contract.functions() + .state() + .call(&|data| self.client.call_contract(block, *address, data)) + .map_err(|e| ErrorKind::Call(format!("Contract call failed {:?}", e)))?; + + self.decrypt(address, &state) + } + + fn get_decrypted_code(&self, address: &Address, block: BlockId) -> Result { + let contract = private::PrivateContract::default(); + let code = contract.functions() + .code() + .call(&|data| self.client.call_contract(block, *address, data)) + .map_err(|e| ErrorKind::Call(format!("Contract call failed {:?}", e)))?; + + self.decrypt(address, &code) + } + + pub fn get_contract_nonce(&self, address: &Address, block: BlockId) -> Result { + let contract = private::PrivateContract::default(); + Ok(contract.functions() + .nonce() + .call(&|data| self.client.call_contract(block, *address, data)) + .map_err(|e| ErrorKind::Call(format!("Contract call failed {:?}", e)))?) + } + + fn snapshot_to_storage(raw: Bytes) -> HashMap { + let items = raw.len() / 64; + (0..items).map(|i| { + let offset = i * 64; + let key = H256::from_slice(&raw[offset..(offset + 32)]); + let value = H256::from_slice(&raw[(offset + 32)..(offset + 64)]); + (key, value) + }).collect() + } + + fn snapshot_from_storage(storage: &HashMap) -> Bytes { + let mut raw = Vec::with_capacity(storage.len() * 64); + for (key, value) in storage { + raw.extend_from_slice(key); + raw.extend_from_slice(value); + }; + raw + } + + pub fn execute_private(&self, transaction: &SignedTransaction, options: TransactOptions, block: BlockId) -> Result, Error> + where + T: Tracer, + V: VMTracer, + { + let mut env_info = self.client.env_info(block).ok_or(ErrorKind::StatePruned)?; + env_info.gas_limit = transaction.gas; + + let mut state = self.client.state_at(block).ok_or(ErrorKind::StatePruned)?; + // TODO: in case of BlockId::Latest these need to operate on the same state + let contract_address = match transaction.action { + Action::Call(ref contract_address) => { + let contract_code = Arc::new(self.get_decrypted_code(contract_address, block)?); + let contract_state = self.get_decrypted_state(contract_address, block)?; + trace!("Patching contract at {:?}, code: {:?}, state: {:?}", contract_address, contract_code, contract_state); + state.patch_account(contract_address, contract_code, Self::snapshot_to_storage(contract_state))?; + Some(*contract_address) + }, + Action::Create => None, + }; + + let engine = self.client.engine(); + let contract_address = contract_address.or({ + let sender = transaction.sender(); + let nonce = state.nonce(&sender)?; + let (new_address, _) = ethcore_contract_address(engine.create_address_scheme(env_info.number), &sender, &nonce, &transaction.data); + Some(new_address) + }); + let result = Executive::new(&mut state, &env_info, engine.machine()).transact_virtual(transaction, options)?; + let (encrypted_code, encrypted_storage) = match contract_address { + None => bail!(ErrorKind::ContractDoesNotExist), + Some(address) => { + let (code, storage) = state.into_account(&address)?; + let enc_code = match code { + Some(c) => Some(self.encrypt(&address, &Self::iv_from_address(&address), &c)?), + None => None, + }; + (enc_code, self.encrypt(&address, &Self::iv_from_transaction(transaction), &Self::snapshot_from_storage(&storage))?) + }, + }; + trace!("Private contract executed. code: {:?}, state: {:?}, result: {:?}", encrypted_code, encrypted_storage, result.output); + Ok(PrivateExecutionResult { + code: encrypted_code, + state: encrypted_storage, + contract_address, + result, + }) + } + + fn generate_constructor(validators: &[Address], code: Bytes, storage: Bytes) -> Bytes { + let constructor_code = DEFAULT_STUB_CONTRACT.from_hex().expect("Default contract code is valid"); + let private = private::PrivateContract::default(); + private.constructor(constructor_code, validators.iter().map(|a| *a).collect::>(), code, storage) + } + + fn generate_set_state_call(signatures: &[Signature], storage: Bytes) -> Bytes { + let private = private::PrivateContract::default(); + private.functions().set_state().input( + storage, + signatures.iter().map(|s| { + let mut v: [u8; 32] = [0; 32]; + v[31] = s.v(); + v + }).collect::>(), + signatures.iter().map(|s| s.r()).collect::>(), + signatures.iter().map(|s| s.s()).collect::>() + ) + } + + /// Returns the key from the key server associated with the contract + pub fn contract_key_id(&self, contract_address: &Address) -> Result { + //current solution uses contract address extended with 0 as id + let contract_address_extended: H256 = contract_address.into(); + + Ok(H256::from_slice(&contract_address_extended)) + } + + /// Create encrypted public contract deployment transaction. + pub fn public_creation_transaction(&self, block: BlockId, source: &SignedTransaction, validators: &[Address], gas_price: U256) -> Result<(Transaction, Option
), Error> { + if let Action::Call(_) = source.action { + bail!(ErrorKind::BadTransactonType); + } + let sender = source.sender(); + let state = self.client.state_at(block).ok_or(ErrorKind::StatePruned)?; + let nonce = state.nonce(&sender)?; + let executed = self.execute_private(source, TransactOptions::with_no_tracing(), block)?; + let gas: u64 = 650000 + + validators.len() as u64 * 30000 + + executed.code.as_ref().map_or(0, |c| c.len() as u64) * 8000 + + executed.state.len() as u64 * 8000; + Ok((Transaction { + nonce: nonce, + action: Action::Create, + gas: gas.into(), + gas_price: gas_price, + value: source.value, + data: Self::generate_constructor(validators, executed.code.unwrap_or_default(), executed.state) + }, + executed.contract_address)) + } + + /// Create encrypted public contract deployment transaction. Returns updated encrypted state. + pub fn execute_private_transaction(&self, block: BlockId, source: &SignedTransaction) -> Result { + if let Action::Create = source.action { + bail!(ErrorKind::BadTransactonType); + } + let result = self.execute_private(source, TransactOptions::with_no_tracing(), block)?; + Ok(result.state) + } + + /// Create encrypted public transaction from private transaction. + pub fn public_transaction(&self, state: Bytes, source: &SignedTransaction, signatures: &[Signature], nonce: U256, gas_price: U256) -> Result { + let gas: u64 = 650000 + state.len() as u64 * 8000 + signatures.len() as u64 * 50000; + Ok(Transaction { + nonce: nonce, + action: source.action.clone(), + gas: gas.into(), + gas_price: gas_price, + value: 0.into(), + data: Self::generate_set_state_call(signatures, state) + }) + } + + /// Call into private contract. + pub fn private_call(&self, block: BlockId, transaction: &SignedTransaction) -> Result { + let result = self.execute_private(transaction, TransactOptions::with_no_tracing(), block)?; + Ok(result.result) + } + + /// Returns private validators for a contract. + pub fn get_validators(&self, block: BlockId, address: &Address) -> Result, Error> { + let contract = private::PrivateContract::default(); + Ok(contract.functions() + .get_validators() + .call(&|data| self.client.call_contract(block, *address, data)) + .map_err(|e| ErrorKind::Call(format!("Contract call failed {:?}", e)))?) + } +} + +pub trait Importer { + /// Process received private transaction + fn import_private_transaction(&self, _rlp: &[u8]) -> Result<(), Error>; + + /// Add signed private transaction into the store + /// + /// Creates corresponding public transaction if last required signature collected and sends it to the chain + fn import_signed_private_transaction(&self, _rlp: &[u8]) -> Result<(), Error>; +} + +// TODO [ToDr] Offload more heavy stuff to the IoService thread. +// It seems that a lot of heavy work (verification) is done in this thread anyway +// it might actually make sense to decouple it from clientService and just use dedicated thread +// for both verification and execution. + +impl Importer for Arc { + fn import_private_transaction(&self, rlp: &[u8]) -> Result<(), Error> { + trace!("Private transaction received"); + let private_tx: PrivateTransaction = Rlp::new(rlp).as_val()?; + let contract = private_tx.contract; + let contract_validators = self.get_validators(BlockId::Latest, &contract)?; + + let validation_account = contract_validators + .iter() + .find(|address| self.validator_accounts.contains(address)); + + match validation_account { + None => { + // TODO [ToDr] This still seems a bit invalid, imho we should still import the transaction to the pool. + // Importing to pool verifies correctness and nonce; here we are just blindly forwarding. + // + // Not for verification, broadcast further to peers + self.broadcast_private_transaction(rlp.into()); + return Ok(()); + }, + Some(&validation_account) => { + let hash = private_tx.hash(); + trace!("Private transaction taken for verification"); + let original_tx = self.extract_original_transaction(private_tx, &contract)?; + trace!("Validating transaction: {:?}", original_tx); + // Verify with the first account available + trace!("The following account will be used for verification: {:?}", validation_account); + let nonce_cache = Default::default(); + self.transactions_for_verification.lock().add_transaction( + original_tx, + contract, + validation_account, + hash, + self.pool_client(&nonce_cache), + )?; + let provider = Arc::downgrade(self); + self.channel.send(ClientIoMessage::execute(move |_| { + if let Some(provider) = provider.upgrade() { + if let Err(e) = provider.process_queue() { + debug!("Unable to process the queue: {}", e); + } + } + })).map_err(|_| ErrorKind::ClientIsMalformed.into()) + } + } + } + + fn import_signed_private_transaction(&self, rlp: &[u8]) -> Result<(), Error> { + let tx: SignedPrivateTransaction = Rlp::new(rlp).as_val()?; + trace!("Signature for private transaction received: {:?}", tx); + let private_hash = tx.private_transaction_hash(); + let desc = match self.transactions_for_signing.lock().get(&private_hash) { + None => { + // TODO [ToDr] Verification (we can't just blindly forward every transaction) + + // Not our transaction, broadcast further to peers + self.broadcast_signed_private_transaction(rlp.into()); + return Ok(()); + }, + Some(desc) => desc, + }; + + let last = self.last_required_signature(&desc, tx.signature())?; + + if last { + let mut signatures = desc.received_signatures.clone(); + signatures.push(tx.signature()); + let rsv: Vec = signatures.into_iter().map(|sign| sign.into_electrum().into()).collect(); + //Create public transaction + let public_tx = self.public_transaction( + desc.state.clone(), + &desc.original_transaction, + &rsv, + desc.original_transaction.nonce, + desc.original_transaction.gas_price + )?; + trace!("Last required signature received, public transaction created: {:?}", public_tx); + //Sign and add it to the queue + let chain_id = desc.original_transaction.chain_id(); + let hash = public_tx.hash(chain_id); + let signer_account = self.signer_account.ok_or_else(|| ErrorKind::SignerAccountNotSet)?; + let password = find_account_password(&self.passwords, &*self.accounts, &signer_account); + let signature = self.accounts.sign(signer_account, password, hash)?; + let signed = SignedTransaction::new(public_tx.with_signature(signature, chain_id))?; + match self.miner.import_own_transaction(&*self.client, signed.into()) { + Ok(_) => trace!("Public transaction added to queue"), + Err(err) => { + trace!("Failed to add transaction to queue, error: {:?}", err); + bail!(err); + } + } + //Remove from store for signing + match self.transactions_for_signing.lock().remove(&private_hash) { + Ok(_) => {} + Err(err) => { + trace!("Failed to remove transaction from signing store, error: {:?}", err); + bail!(err); + } + } + } else { + //Add signature to the store + match self.transactions_for_signing.lock().add_signature(&private_hash, tx.signature()) { + Ok(_) => trace!("Signature stored for private transaction"), + Err(err) => { + trace!("Failed to add signature to signing store, error: {:?}", err); + bail!(err); + } + } + } + Ok(()) + } +} + +/// Try to unlock account using stored password, return found password if any +fn find_account_password(passwords: &Vec, account_provider: &AccountProvider, account: &Address) -> Option { + for password in passwords { + if let Ok(true) = account_provider.test_password(account, password) { + return Some(password.clone()); + } + } + None +} + +impl ChainNotify for Provider { + fn new_blocks(&self, imported: Vec, _invalid: Vec, _route: ChainRoute, _sealed: Vec, _proposed: Vec, _duration: Duration) { + if !imported.is_empty() { + trace!("New blocks imported, try to prune the queue"); + if let Err(err) = self.process_queue() { + trace!("Cannot prune private transactions queue. error: {:?}", err); + } + } + } +} diff --git a/ethcore/private-tx/src/messages.rs b/ethcore/private-tx/src/messages.rs new file mode 100644 index 0000000000000000000000000000000000000000..57362e7ce6273898c83c625766d6dedb598d1997 --- /dev/null +++ b/ethcore/private-tx/src/messages.rs @@ -0,0 +1,76 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use ethereum_types::{H256, U256, Address}; +use bytes::Bytes; +use hash::keccak; +use rlp::Encodable; +use ethkey::Signature; +use transaction::signature::{add_chain_replay_protection, check_replay_protection}; + +/// Message with private transaction encrypted +#[derive(Default, Debug, Clone, PartialEq, RlpEncodable, RlpDecodable, Eq)] +pub struct PrivateTransaction { + /// Encrypted data + pub encrypted: Bytes, + /// Address of the contract + pub contract: Address, +} + +impl PrivateTransaction { + /// Compute hash on private transaction + pub fn hash(&self) -> H256 { + keccak(&*self.rlp_bytes()) + } +} + +/// Message about private transaction's signing +#[derive(Default, Debug, Clone, PartialEq, RlpEncodable, RlpDecodable, Eq)] +pub struct SignedPrivateTransaction { + /// Hash of the corresponding private transaction + private_transaction_hash: H256, + /// Signature of the validator + /// The V field of the signature + v: u64, + /// The R field of the signature + r: U256, + /// The S field of the signature + s: U256, +} + +impl SignedPrivateTransaction { + /// Construct a signed private transaction message + pub fn new(private_transaction_hash: H256, sig: Signature, chain_id: Option) -> Self { + SignedPrivateTransaction { + private_transaction_hash: private_transaction_hash, + r: sig.r().into(), + s: sig.s().into(), + v: add_chain_replay_protection(sig.v() as u64, chain_id), + } + } + + pub fn standard_v(&self) -> u8 { check_replay_protection(self.v) } + + /// Construct a signature object from the sig. + pub fn signature(&self) -> Signature { + Signature::from_rsv(&self.r.into(), &self.s.into(), self.standard_v()) + } + + /// Get the hash of of the original transaction. + pub fn private_transaction_hash(&self) -> H256 { + self.private_transaction_hash + } +} diff --git a/ethcore/private-tx/src/private_transactions.rs b/ethcore/private-tx/src/private_transactions.rs new file mode 100644 index 0000000000000000000000000000000000000000..fcc6da514ed4a8813b0ed041b805fd4a5add71df --- /dev/null +++ b/ethcore/private-tx/src/private_transactions.rs @@ -0,0 +1,208 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use std::sync::Arc; +use std::collections::{HashMap, HashSet}; + +use bytes::Bytes; +use ethcore_miner::pool; +use ethereum_types::{H256, U256, Address}; +use ethkey::Signature; +use transaction::{UnverifiedTransaction, SignedTransaction}; + +use error::{Error, ErrorKind}; + +/// Maximum length for private transactions queues. +const MAX_QUEUE_LEN: usize = 8312; + +/// Desriptor for private transaction stored in queue for verification +#[derive(Default, Debug, Clone, PartialEq, Eq)] +pub struct PrivateTransactionDesc { + /// Hash of the private transaction + pub private_hash: H256, + /// Contract's address used in private transaction + pub contract: Address, + /// Address that should be used for verification + pub validator_account: Address, +} + +/// Storage for private transactions for verification +pub struct VerificationStore { + /// Descriptors for private transactions in queue for verification with key - hash of the original transaction + descriptors: HashMap, + /// Queue with transactions for verification + /// + /// TODO [ToDr] Might actually be better to use `txpool` directly and: + /// 1. Store descriptors inside `VerifiedTransaction` + /// 2. Use custom `ready` implementation to only fetch one transaction per sender. + /// 3. Get rid of passing dummy `block_number` and `timestamp` + transactions: pool::TransactionQueue, +} + +impl Default for VerificationStore { + fn default() -> Self { + VerificationStore { + descriptors: Default::default(), + transactions: pool::TransactionQueue::new( + pool::Options { + max_count: MAX_QUEUE_LEN, + max_per_sender: MAX_QUEUE_LEN / 10, + max_mem_usage: 8 * 1024 * 1024, + }, + pool::verifier::Options { + // TODO [ToDr] This should probably be based on some real values? + minimal_gas_price: 0.into(), + block_gas_limit: 8_000_000.into(), + tx_gas_limit: U256::max_value(), + }, + pool::PrioritizationStrategy::GasPriceOnly, + ) + } + } +} + +impl VerificationStore { + /// Adds private transaction for verification into the store + pub fn add_transaction( + &mut self, + transaction: UnverifiedTransaction, + contract: Address, + validator_account: Address, + private_hash: H256, + client: C, + ) -> Result<(), Error> { + if self.descriptors.len() > MAX_QUEUE_LEN { + bail!(ErrorKind::QueueIsFull); + } + + let transaction_hash = transaction.hash(); + if self.descriptors.get(&transaction_hash).is_some() { + bail!(ErrorKind::PrivateTransactionAlreadyImported); + } + + let results = self.transactions.import( + client, + vec![pool::verifier::Transaction::Unverified(transaction)], + ); + + // Verify that transaction was imported + results.into_iter() + .next() + .expect("One transaction inserted; one result returned; qed")?; + + self.descriptors.insert(transaction_hash, PrivateTransactionDesc { + private_hash, + contract, + validator_account, + }); + + Ok(()) + } + + /// Returns transactions ready for verification + /// Returns only one transaction per sender because several cannot be verified in a row without verification from other peers + pub fn ready_transactions(&self, client: C) -> Vec> { + // We never store PendingTransactions and we don't use internal cache, + // so we don't need to provide real block number of timestamp here + let block_number = 0; + let timestamp = 0; + let nonce_cap = None; + + self.transactions.collect_pending(client, block_number, timestamp, nonce_cap, |transactions| { + // take only one transaction per sender + let mut senders = HashSet::with_capacity(self.descriptors.len()); + transactions.filter(move |tx| senders.insert(tx.signed().sender())).collect() + }) + } + + /// Returns descriptor of the corresponding private transaction + pub fn private_transaction_descriptor(&self, transaction_hash: &H256) -> Result<&PrivateTransactionDesc, Error> { + self.descriptors.get(transaction_hash).ok_or(ErrorKind::PrivateTransactionNotFound.into()) + } + + /// Remove transaction from the queue for verification + pub fn remove_private_transaction(&mut self, transaction_hash: &H256) { + self.descriptors.remove(transaction_hash); + self.transactions.remove(&[*transaction_hash], true); + } +} + +/// Desriptor for private transaction stored in queue for signing +#[derive(Debug, Clone)] +pub struct PrivateTransactionSigningDesc { + /// Original unsigned transaction + pub original_transaction: SignedTransaction, + /// Supposed validators from the contract + pub validators: Vec
, + /// Already obtained signatures + pub received_signatures: Vec, + /// State after transaction execution to compare further with received from validators + pub state: Bytes, + /// Build-in nonce of the contract + pub contract_nonce: U256, +} + +/// Storage for private transactions for signing +#[derive(Default)] +pub struct SigningStore { + /// Transactions and descriptors for signing + transactions: HashMap, +} + +impl SigningStore { + /// Adds new private transaction into the store for signing + pub fn add_transaction( + &mut self, + private_hash: H256, + transaction: SignedTransaction, + validators: Vec
, + state: Bytes, + contract_nonce: U256, + ) -> Result<(), Error> { + if self.transactions.len() > MAX_QUEUE_LEN { + bail!(ErrorKind::QueueIsFull); + } + + self.transactions.insert(private_hash, PrivateTransactionSigningDesc { + original_transaction: transaction.clone(), + validators: validators.clone(), + received_signatures: Vec::new(), + state, + contract_nonce, + }); + Ok(()) + } + + /// Get copy of private transaction's description from the storage + pub fn get(&self, private_hash: &H256) -> Option { + self.transactions.get(private_hash).cloned() + } + + /// Removes desc from the store (after verification is completed) + pub fn remove(&mut self, private_hash: &H256) -> Result<(), Error> { + self.transactions.remove(private_hash); + Ok(()) + } + + /// Adds received signature for the stored private transaction + pub fn add_signature(&mut self, private_hash: &H256, signature: Signature) -> Result<(), Error> { + let desc = self.transactions.get_mut(private_hash).ok_or_else(|| ErrorKind::PrivateTransactionNotFound)?; + if !desc.received_signatures.contains(&signature) { + desc.received_signatures.push(signature); + } + Ok(()) + } +} diff --git a/ethcore/private-tx/tests/private_contract.rs b/ethcore/private-tx/tests/private_contract.rs new file mode 100644 index 0000000000000000000000000000000000000000..bc678b1ab560a37911289d206be851668152600c --- /dev/null +++ b/ethcore/private-tx/tests/private_contract.rs @@ -0,0 +1,146 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Contract for private transactions tests. + +extern crate rustc_hex; +extern crate ethcore; +extern crate ethkey; +extern crate keccak_hash as hash; +extern crate ethcore_io; +extern crate ethcore_logger; +extern crate ethcore_private_tx; +extern crate ethcore_transaction; + +#[macro_use] +extern crate log; + +use std::sync::Arc; +use rustc_hex::FromHex; + +use ethcore::CreateContractAddress; +use ethcore::account_provider::AccountProvider; +use ethcore::client::BlockChainClient; +use ethcore::client::BlockId; +use ethcore::executive::{contract_address}; +use ethcore::miner::Miner; +use ethcore::test_helpers::{generate_dummy_client, push_block_with_transactions}; +use ethcore_transaction::{Transaction, Action}; +use ethkey::{Secret, KeyPair, Signature}; +use hash::keccak; + +use ethcore_private_tx::{NoopEncryptor, Provider, ProviderConfig}; + +#[test] +fn private_contract() { + // This uses a simple private contract: contract Test1 { bytes32 public x; function setX(bytes32 _x) { x = _x; } } + ethcore_logger::init_log(); + let client = generate_dummy_client(0); + let chain_id = client.signing_chain_id(); + let key1 = KeyPair::from_secret(Secret::from("0000000000000000000000000000000000000000000000000000000000000011")).unwrap(); + let _key2 = KeyPair::from_secret(Secret::from("0000000000000000000000000000000000000000000000000000000000000012")).unwrap(); + let key3 = KeyPair::from_secret(Secret::from("0000000000000000000000000000000000000000000000000000000000000013")).unwrap(); + let key4 = KeyPair::from_secret(Secret::from("0000000000000000000000000000000000000000000000000000000000000014")).unwrap(); + let ap = Arc::new(AccountProvider::transient_provider()); + ap.insert_account(key1.secret().clone(), "").unwrap(); + ap.insert_account(key3.secret().clone(), "").unwrap(); + ap.insert_account(key4.secret().clone(), "").unwrap(); + + let config = ProviderConfig{ + validator_accounts: vec![key3.address(), key4.address()], + signer_account: None, + passwords: vec!["".into()], + }; + + let io = ethcore_io::IoChannel::disconnected(); + let miner = Arc::new(Miner::new_for_tests(&::ethcore::spec::Spec::new_test(), None)); + let pm = Arc::new(Provider::new( + client.clone(), + miner, + ap.clone(), + Box::new(NoopEncryptor::default()), + config, + io, + )); + + let (address, _) = contract_address(CreateContractAddress::FromSenderAndNonce, &key1.address(), &0.into(), &[]); + + trace!("Creating private contract"); + let private_contract_test = "6060604052341561000f57600080fd5b60d88061001d6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630c55699c146046578063bc64b76d14607457600080fd5b3415605057600080fd5b60566098565b60405180826000191660001916815260200191505060405180910390f35b3415607e57600080fd5b6096600480803560001916906020019091905050609e565b005b60005481565b8060008160001916905550505600a165627a7a723058206acbdf4b15ca4c2d43e1b1879b830451a34f1e9d02ff1f2f394d8d857e79d2080029".from_hex().unwrap(); + let mut private_create_tx = Transaction::default(); + private_create_tx.action = Action::Create; + private_create_tx.data = private_contract_test; + private_create_tx.gas = 200000.into(); + let private_create_tx_signed = private_create_tx.sign(&key1.secret(), None); + let validators = vec![key3.address(), key4.address()]; + let (public_tx, _) = pm.public_creation_transaction(BlockId::Latest, &private_create_tx_signed, &validators, 0.into()).unwrap(); + let public_tx = public_tx.sign(&key1.secret(), chain_id); + trace!("Transaction created. Pushing block"); + push_block_with_transactions(&client, &[public_tx]); + + trace!("Modifying private state"); + let mut private_tx = Transaction::default(); + private_tx.action = Action::Call(address.clone()); + private_tx.data = "bc64b76d2a00000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap(); //setX(42) + private_tx.gas = 120000.into(); + private_tx.nonce = 1.into(); + let private_tx = private_tx.sign(&key1.secret(), None); + let private_contract_nonce = pm.get_contract_nonce(&address, BlockId::Latest).unwrap(); + let private_state = pm.execute_private_transaction(BlockId::Latest, &private_tx).unwrap(); + let nonced_state_hash = pm.calculate_state_hash(&private_state, private_contract_nonce); + let signatures: Vec<_> = [&key3, &key4].iter().map(|k| + Signature::from(::ethkey::sign(&k.secret(), &nonced_state_hash).unwrap().into_electrum())).collect(); + let public_tx = pm.public_transaction(private_state, &private_tx, &signatures, 1.into(), 0.into()).unwrap(); + let public_tx = public_tx.sign(&key1.secret(), chain_id); + push_block_with_transactions(&client, &[public_tx]); + + trace!("Querying private state"); + let mut query_tx = Transaction::default(); + query_tx.action = Action::Call(address.clone()); + query_tx.data = "0c55699c".from_hex().unwrap(); // getX + query_tx.gas = 50000.into(); + query_tx.nonce = 2.into(); + let query_tx = query_tx.sign(&key1.secret(), chain_id); + let result = pm.private_call(BlockId::Latest, &query_tx).unwrap(); + assert_eq!(&result.output[..], &("2a00000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap()[..])); + assert_eq!(pm.get_validators(BlockId::Latest, &address).unwrap(), validators); + + // Now try modification with just one signature + trace!("Modifying private state"); + let mut private_tx = Transaction::default(); + private_tx.action = Action::Call(address.clone()); + private_tx.data = "bc64b76d2b00000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap(); //setX(43) + private_tx.gas = 120000.into(); + private_tx.nonce = 2.into(); + let private_tx = private_tx.sign(&key1.secret(), None); + let private_state = pm.execute_private_transaction(BlockId::Latest, &private_tx).unwrap(); + let private_state_hash = keccak(&private_state); + let signatures: Vec<_> = [&key4].iter().map(|k| + Signature::from(::ethkey::sign(&k.secret(), &private_state_hash).unwrap().into_electrum())).collect(); + let public_tx = pm.public_transaction(private_state, &private_tx, &signatures, 2.into(), 0.into()).unwrap(); + let public_tx = public_tx.sign(&key1.secret(), chain_id); + push_block_with_transactions(&client, &[public_tx]); + + trace!("Querying private state"); + let mut query_tx = Transaction::default(); + query_tx.action = Action::Call(address.clone()); + query_tx.data = "0c55699c".from_hex().unwrap(); // getX + query_tx.gas = 50000.into(); + query_tx.nonce = 3.into(); + let query_tx = query_tx.sign(&key1.secret(), chain_id); + let result = pm.private_call(BlockId::Latest, &query_tx).unwrap(); + assert_eq!(result.output, "2a00000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap()); +} diff --git a/ethcore/res/authority_round_block_reward_contract.json b/ethcore/res/authority_round_block_reward_contract.json new file mode 100644 index 0000000000000000000000000000000000000000..31957731232e09ac957a7bc02915685751ce4ebd --- /dev/null +++ b/ethcore/res/authority_round_block_reward_contract.json @@ -0,0 +1,61 @@ +{ + "name": "TestAuthorityRoundBlockRewardContract", + "engine": { + "authorityRound": { + "params": { + "stepDuration": 1, + "startStep": 2, + "validators": { + "list": [ + "0x7d577a597b2742b498cb5cf0c26cdcd726d39e6e", + "0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1" + ] + }, + "immediateTransitions": true, + "emptyStepsTransition": "1", + "maximumEmptySteps": "2", + "blockRewardContractAddress": "0x0000000000000000000000000000000000000042" + } + } + }, + "params": { + "gasLimitBoundDivisor": "0x0400", + "accountStartNonce": "0x0", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "networkID" : "0x69", + "eip140Transition": "0x0", + "eip211Transition": "0x0", + "eip214Transition": "0x0", + "eip658Transition": "0x0" + }, + "genesis": { + "seal": { + "authorityRound": { + "step": "0x0", + "signature": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + } + }, + "difficulty": "0x20000", + "author": "0x0000000000000000000000000000000000000000", + "timestamp": "0x00", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x", + "gasLimit": "0x222222" + }, + "accounts": { + "0000000000000000000000000000000000000001": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, + "0000000000000000000000000000000000000002": { "balance": "1", "nonce": "1048576", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, + "0000000000000000000000000000000000000003": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, + "0000000000000000000000000000000000000004": { "balance": "1", "nonce": "1048576", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, + "0000000000000000000000000000000000000005": { "balance": "1", "builtin": { "name": "modexp", "activate_at": 0, "pricing": { "modexp": { "divisor": 20 } } } }, + "0000000000000000000000000000000000000006": { "balance": "1", "builtin": { "name": "alt_bn128_add", "activate_at": 0, "pricing": { "linear": { "base": 500, "word": 0 } } } }, + "0000000000000000000000000000000000000007": { "balance": "1", "builtin": { "name": "alt_bn128_mul", "activate_at": 0, "pricing": { "linear": { "base": 40000, "word": 0 } } } }, + "0000000000000000000000000000000000000008": { "balance": "1", "builtin": { "name": "alt_bn128_pairing", "activate_at": 0, "pricing": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 } } } }, + "9cce34f7ab185c7aba1b7c8140d620b4bda941d6": { "balance": "1606938044258990275541962092341162602522202993782792835301376", "nonce": "1048576" }, + "0000000000000000000000000000000000000042": { + "balance": "1", + "constructor": "6060604052341561000f57600080fd5b6102b88061001e6000396000f300606060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063f91c289814610046575b600080fd5b341561005157600080fd5b610086600480803590602001908201803590602001919091929080359060200190820180359060200191909192905050610125565b604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b838110156100cd5780820151818401526020810190506100b2565b50505050905001838103825284818151815260200191508051906020019060200280838360005b8381101561010f5780820151818401526020810190506100f4565b5050505090500194505050505060405180910390f35b61012d610264565b610135610278565b61013d610278565b600073fffffffffffffffffffffffffffffffffffffffe73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561018d57600080fd5b85859050888890501415156101a157600080fd5b878790506040518059106101b25750595b90808252806020026020018201604052509150600090505b815181101561021d5785858281811015156101e157fe5b9050602002013561ffff166103e80161ffff16828281518110151561020257fe5b906020019060200201818152505080806001019150506101ca565b878783828280806020026020016040519081016040528093929190818152602001838360200280828437820191505050505050915090915093509350505094509492505050565b602060405190810160405280600081525090565b6020604051908101604052806000815250905600a165627a7a723058201da0f164e75517fb8baf51f030b904032cb748334938e7386f63025bfb23f3de0029" + } + } +} diff --git a/ethcore/res/contracts/block_reward.json b/ethcore/res/contracts/block_reward.json new file mode 100644 index 0000000000000000000000000000000000000000..9209967f3ace3b7e4d2b131a1b1a675ba07c5738 --- /dev/null +++ b/ethcore/res/contracts/block_reward.json @@ -0,0 +1,29 @@ +[ + { + "constant": false, + "inputs": [ + { + "name": "benefactors", + "type": "address[]" + }, + { + "name": "kind", + "type": "uint16[]" + } + ], + "name": "reward", + "outputs": [ + { + "name": "", + "type": "address[]" + }, + { + "name": "", + "type": "uint256[]" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/ethcore/res/contracts/tx_acl.json b/ethcore/res/contracts/tx_acl.json index cc924cafb81d9cbe6c023b87081cbc386c41c7b9..e110797d96fb3107d981aaee113072a6a21fab98 100644 --- a/ethcore/res/contracts/tx_acl.json +++ b/ethcore/res/contracts/tx_acl.json @@ -1 +1 @@ -[{"constant":true,"inputs":[{"name":"sender","type":"address"}],"name":"allowedTxTypes","outputs":[{"name":"","type":"uint32"}],"payable":false,"stateMutability":"nonpayable","type":"function"}] +[ { "constant": true, "inputs": [], "name": "contractNameHash", "outputs": [ { "name": "", "type": "bytes32" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [], "name": "contractName", "outputs": [ { "name": "", "type": "string" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [], "name": "contractVersion", "outputs": [ { "name": "", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [ { "name": "sender", "type": "address" }, { "name": "to", "type": "address" }, { "name": "value", "type": "uint256" } ], "name": "allowedTxTypes", "outputs": [ { "name": "", "type": "uint32" }, { "name": "", "type": "bool" } ], "payable": false, "stateMutability": "view", "type": "function" } ] diff --git a/ethcore/res/contracts/tx_acl_deprecated.json b/ethcore/res/contracts/tx_acl_deprecated.json new file mode 100644 index 0000000000000000000000000000000000000000..cc924cafb81d9cbe6c023b87081cbc386c41c7b9 --- /dev/null +++ b/ethcore/res/contracts/tx_acl_deprecated.json @@ -0,0 +1 @@ +[{"constant":true,"inputs":[{"name":"sender","type":"address"}],"name":"allowedTxTypes","outputs":[{"name":"","type":"uint32"}],"payable":false,"stateMutability":"nonpayable","type":"function"}] diff --git a/ethcore/res/ethereum/byzantium_test.json b/ethcore/res/ethereum/byzantium_test.json index 40c7e465991c46c30693b7ee2b88e3c3e5897ae9..90c92bbff55a9865755a28756e3f51e30d0bfb19 100644 --- a/ethcore/res/ethereum/byzantium_test.json +++ b/ethcore/res/ethereum/byzantium_test.json @@ -8,10 +8,6 @@ "durationLimit": "0x0d", "blockReward": "0x4563918244F40000", "homesteadTransition": "0x0", - "eip150Transition": "0x0", - "eip160Transition": "0x0", - "eip161abcTransition": "0x0", - "eip161dTransition": "0x0", "eip649Reward": "0x29A2241AF62C0000", "eip100bTransition": "0x0", "eip649Transition": "0x0" @@ -27,6 +23,10 @@ "networkID" : "0x1", "maxCodeSize": 24576, "maxCodeSizeTransition": "0x0", + "eip150Transition": "0x0", + "eip160Transition": "0x0", + "eip161abcTransition": "0x0", + "eip161dTransition": "0x0", "eip98Transition": "0xffffffffffffffff", "eip140Transition": "0x0", "eip211Transition": "0x0", diff --git a/ethcore/res/ethereum/classic.json b/ethcore/res/ethereum/classic.json index a4c6fe9f227737239c53cc0555c1710194a1baf2..17d9cd2bc0f8d8d5d69dd8f748543431e28e4ea2 100644 --- a/ethcore/res/ethereum/classic.json +++ b/ethcore/res/ethereum/classic.json @@ -9,13 +9,9 @@ "durationLimit": "0x0d", "blockReward": "0x4563918244F40000", "homesteadTransition": 1150000, - "eip150Transition": 2500000, - "eip160Transition": 3000000, "ecip1010PauseTransition": 3000000, "ecip1010ContinueTransition": 5000000, "ecip1017EraRounds": 5000000, - "eip161abcTransition": "0x7fffffffffffffff", - "eip161dTransition": "0x7fffffffffffffff", "bombDefuseTransition": 5900000 } } @@ -30,6 +26,10 @@ "chainID": "0x3d", "forkBlock": "0x1d4c00", "forkCanonHash": "0x94365e3a8c0b35089c1d1195081fe7489b528a84b22199c916180db8b28ade7f", + "eip150Transition": 2500000, + "eip160Transition": 3000000, + "eip161abcTransition": "0x7fffffffffffffff", + "eip161dTransition": "0x7fffffffffffffff", "eip155Transition": 3000000, "eip98Transition": "0x7fffffffffffff", "eip86Transition": "0x7fffffffffffff" @@ -57,7 +57,8 @@ "enode://814920f1ec9510aa9ea1c8f79d8b6e6a462045f09caa2ae4055b0f34f7416fca6facd3dd45f1cf1673c0209e0503f02776b8ff94020e98b6679a0dc561b4eba0@104.154.136.117:30303", "enode://72e445f4e89c0f476d404bc40478b0df83a5b500d2d2e850e08eb1af0cd464ab86db6160d0fde64bd77d5f0d33507ae19035671b3c74fec126d6e28787669740@104.198.71.200:30303", "enode://39abab9d2a41f53298c0c9dc6bbca57b0840c3ba9dccf42aa27316addc1b7e56ade32a0a9f7f52d6c5db4fe74d8824bcedfeaecf1a4e533cacb71cf8100a9442@144.76.238.49:30303", - "enode://f50e675a34f471af2438b921914b5f06499c7438f3146f6b8936f1faeb50b8a91d0d0c24fb05a66f05865cd58c24da3e664d0def806172ddd0d4c5bdbf37747e@144.76.238.49:30306" + "enode://f50e675a34f471af2438b921914b5f06499c7438f3146f6b8936f1faeb50b8a91d0d0c24fb05a66f05865cd58c24da3e664d0def806172ddd0d4c5bdbf37747e@144.76.238.49:30306", + "enode://83b33409349ffa25e150555f7b4f8deebc68f3d34d782129dc3c8ba07b880c209310a4191e1725f2f6bef59bce9452d821111eaa786deab08a7e6551fca41f4f@159.89.223.6:30303" ], "accounts": { "0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, diff --git a/ethcore/res/ethereum/constantinople_test.json b/ethcore/res/ethereum/constantinople_test.json index 7b137e86fa0732b9aa25ebbcf13ac4bc42f6a450..155b06507d0fed5b8da7ef31e6cd061dc49aa64d 100644 --- a/ethcore/res/ethereum/constantinople_test.json +++ b/ethcore/res/ethereum/constantinople_test.json @@ -8,10 +8,6 @@ "durationLimit": "0x0d", "blockReward": "0x4563918244F40000", "homesteadTransition": "0x0", - "eip150Transition": "0x0", - "eip160Transition": "0x0", - "eip161abcTransition": "0x0", - "eip161dTransition": "0x0", "eip649Reward": "0x29A2241AF62C0000", "eip100bTransition": "0x0", "eip649Transition": "0x0" @@ -28,6 +24,10 @@ "maxCodeSize": 24576, "maxCodeSizeTransition": "0x0", "eip98Transition": "0xffffffffffffffff", + "eip150Transition": "0x0", + "eip160Transition": "0x0", + "eip161abcTransition": "0x0", + "eip161dTransition": "0x0", "eip140Transition": "0x0", "eip210Transition": "0x0", "eip211Transition": "0x0", diff --git a/ethcore/res/ethereum/easthub.json b/ethcore/res/ethereum/easthub.json new file mode 100644 index 0000000000000000000000000000000000000000..e7e1a6e708e6a85cf453ee2407a17fc691a42442 --- /dev/null +++ b/ethcore/res/ethereum/easthub.json @@ -0,0 +1,89 @@ +{ + "name": "Easthub", + "dataDir": "easthub", + "engine": { + "Ethash": { + "params": { + "minimumDifficulty": "0x020000", + "difficultyBoundDivisor": "0x0800", + "durationLimit": "0x0d", + "blockReward": "0x2B5E3AF16B1880000", + "homesteadTransition": "0x0", + "bombDefuseTransition": "0x0", + "ecip1017EraRounds": 5000000 + } + } + }, + "params": { + "gasLimitBoundDivisor": "0x0400", + "registrar": "0x0000000000000000000000000000000000000000", + "accountStartNonce": "0x00", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "networkID": "0x7", + "chainID": "0x7", + "eip150Transition": "0x0", + "eip160Transition": "0x0", + "eip155Transition": "0x0", + "eip161abcTransition": "0x7fffffffffffffff", + "eip161dTransition": "0x7fffffffffffffff", + "eip98Transition": "0x7fffffffffffff", + "eip86Transition": "0x7fffffffffffff" + }, + "genesis": { + "seal": { + "ethereum": { + "nonce": "0x0000000000000042", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + }, + "difficulty": "0x0400000000", + "author": "0x0000000000000000000000000000000000000000", + "timestamp": "0x00", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x323031382045617374636f696e2050726f6a656374", + "gasLimit": "0x1388" + }, + "nodes": [ + "enode://ca57e40edb95a08a81b85a91e91099a0aaab777ad329ea7f3f772bc0fd511a276a5d84944725d181ff80f8c7dc1034814bff25b9723b03363d48617fed4b15f0@13.125.109.174:30303", + "enode://57254e23a7e5fe1e081ee5d1b236e37735a120660daeb4bf1fec6943a82c915c5b6fad23eeb1a43a27c23f236e084e8051aaa28f7d4139149f844747facb62bb@18.217.39.51:30303", + "enode://ef248f327c73c0318f4d51a62270b0612f3c4a4fd04b77d04854dc355980e137708d1e48811bc91387b0d7eb85cf447d8bbc095404f39bb7064e76751bda9cd4@52.221.160.236:30303", + "enode://bf6f0e37dd733cf04f2b079c753d2dea7cc7c59d8637eff9a8e63e17d08e2bfc91229fbb2dff08fe6ee12e51c1b6f8ed969d7042b89d77029e7ea02b05e17be3@18.197.47.177:30303" + ], + "accounts": { + "0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, + "0000000000000000000000000000000000000002": { "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, + "0000000000000000000000000000000000000003": { "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, + "0000000000000000000000000000000000000004": { "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, + "20c1252a8cb33a7a9a257b2a4cfeed8daf87c847": { + "balance": "100000000000000000000000000" + }, + "9dcd37c8e5aea3a0d37c5d0a2db683362d81febd": { + "balance": "100000000000000000000000000" + }, + "9eff080302333f44a60bfd8c33bd63015c6d921b": { + "balance": "100000000000000000000000000" + }, + "c1df2e5de98d5c41fec0642dc302971f5d3500bd": { + "balance": "100000000000000000000000000" + }, + "2e0fb67cd1d029cbaea4b74c361efcc06b3105fd": { + "balance": "100000000000000000000000000" + }, + "2b6425cc3cd90654f077889ef7262ac2f5846460": { + "balance": "100000000000000000000000000" + }, + "28562041230c6d575e233e4ed1b35c514884d964": { + "balance": "100000000000000000000000000" + }, + "16eb6896a5a83d39ac762d79d21f825f5f980d12": { + "balance": "100000000000000000000000000" + }, + "f09e3f1de27dd03a1ac0a021b2d9e45bde1b360c": { + "balance": "100000000000000000000000000" + }, + "2d87547819c6433f208ee3096161cdb2835a2333": { + "balance": "100000000000000000000000000" + } + } +} diff --git a/ethcore/res/ethereum/eip150_test.json b/ethcore/res/ethereum/eip150_test.json index 60157c5716804ee7fda43b6a7dd8a094b75f94e7..baf9c1b7bfc3884a1455fdf90b050f4a065ad723 100644 --- a/ethcore/res/ethereum/eip150_test.json +++ b/ethcore/res/ethereum/eip150_test.json @@ -7,11 +7,7 @@ "difficultyBoundDivisor": "0x0800", "durationLimit": "0x0d", "blockReward": "0x4563918244F40000", - "homesteadTransition": "0x0", - "eip150Transition": "0x0", - "eip160Transition": "0x7fffffffffffffff", - "eip161abcTransition": "0x7fffffffffffffff", - "eip161dTransition": "0x7fffffffffffffff" + "homesteadTransition": "0x0" } } }, @@ -22,6 +18,10 @@ "maximumExtraDataSize": "0x20", "minGasLimit": "0x1388", "networkID" : "0x1", + "eip150Transition": "0x0", + "eip160Transition": "0x7fffffffffffffff", + "eip161abcTransition": "0x7fffffffffffffff", + "eip161dTransition": "0x7fffffffffffffff", "eip98Transition": "0x7fffffffffffffff", "eip86Transition": "0x7fffffffffffffff", "eip155Transition": "0x7fffffffffffffff", diff --git a/ethcore/res/ethereum/eip161_test.json b/ethcore/res/ethereum/eip161_test.json index ac1c0a5d1e3cc60fbb6ed128256209d1889d6ce4..079ce7d55a23a4030086fccfa83f24c4680e02f2 100644 --- a/ethcore/res/ethereum/eip161_test.json +++ b/ethcore/res/ethereum/eip161_test.json @@ -7,11 +7,7 @@ "difficultyBoundDivisor": "0x0800", "durationLimit": "0x0d", "blockReward": "0x4563918244F40000", - "homesteadTransition": "0x0", - "eip150Transition": "0x0", - "eip160Transition": "0x0", - "eip161abcTransition": "0x0", - "eip161dTransition": "0x0" + "homesteadTransition": "0x0" } } }, @@ -22,6 +18,10 @@ "maximumExtraDataSize": "0x20", "minGasLimit": "0x1388", "networkID" : "0x1", + "eip150Transition": "0x0", + "eip160Transition": "0x0", + "eip161abcTransition": "0x0", + "eip161dTransition": "0x0", "eip98Transition": "0x7fffffffffffffff", "eip86Transition": "0x7fffffffffffffff", "eip155Transition": "0x7fffffffffffffff", diff --git a/ethcore/res/ethereum/ellaism.json b/ethcore/res/ethereum/ellaism.json index 3036992eb7bae4fba69fa91a9a9e893ab728c717..c3107bbe4690ecf03fe0ffaa7e392bd7077cfa42 100644 --- a/ethcore/res/ethereum/ellaism.json +++ b/ethcore/res/ethereum/ellaism.json @@ -1,72 +1,74 @@ { - "name": "Ellaism", - "dataDir": "ellaism", - "engine": { - "Ethash": { - "params": { - "minimumDifficulty": "0x020000", - "difficultyBoundDivisor": "0x0800", - "durationLimit": "0x0d", - "blockReward": "0x4563918244F40000", - "homesteadTransition": "0x0", - "bombDefuseTransition": "0x0", - "eip150Transition": "0x0", - "eip160Transition": "0x0", - "ecip1017EraRounds": 10000000, - - "eip161abcTransition": "0x7fffffffffffffff", - "eip161dTransition": "0x7fffffffffffffff" - } - } - }, - "params": { - "gasLimitBoundDivisor": "0x0400", - "registrar": "0x3bb2bb5c6c9c9b7f4EF430b47Dc7e026310042ea", - "accountStartNonce": "0x00", - "maximumExtraDataSize": "0x20", - "minGasLimit": "0x1388", - "networkID": "0x40", - "chainID": "0x40", - "eip155Transition": "0x0", - "eip98Transition": "0x7fffffffffffff", - "eip86Transition": "0x7fffffffffffff" - }, - "genesis": { - "seal": { - "ethereum": { - "nonce": "0x0000000000000040", - "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" - } - }, - "difficulty": "0x40000000", - "author": "0x0000000000000000000000000000000000000000", - "timestamp": "0x00", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000", - "gasLimit": "0x1388" - }, - "nodes": [ - "enode://98b48cc7149326d00a57994ad55014a095a3e5cd4f0144cd7b034fb667d8e8017082bd90047d72c4403798b8ece11a33bd2e344d6500faba30889ebcfa5316fa@172.104.163.204:30303", - "enode://834246cc2a7584df29ccdcf3b5366f118a0e291264980376769e809665a02c4caf0d68c43eecf8390dbeaf861823b05583807af0a62542a1f3f717046b958a76@45.77.106.33:30303", - "enode://d1373d7187a20d695220fafb5746a289786bad92469d6bbe800f07572f8f444479f507cfcb8fcd8f5bee5dd44efb6185df4a1e4f1a7170408ada9876ef5b3fe4@178.79.189.58:30303", - "enode://d8059dcb137cb52b8960ca82613eeba1d121105572decd8f1d3ea22b09070645eeab548d2a3cd2914f206e1331c7870bd2bd5a231ebac6b3d4886ec3b8e627e5@173.212.216.105:30303", - "enode://5a89c8664d29a321fd4cb1b55b0f0be832ce376b5e7feb14a2073fdbd9bd7b7394169ed289dd991112b42ecfb74ea36e436bc72a1c99dcdb50d96eaf3b0ed254@213.136.91.42:30303", - "enode://9215ad77bd081e35013cb42a8ceadff9d8e94a78fcc680dff1752a54e7484badff0904e331c4b40a68be593782e55acfd800f076d22f9d2832e8483733ade149@213.14.82.125:30303", - "enode://07913818dafbadf44d4fc796fa414ec1d720ecfb087eff37efbe7134556658e92351559de788fa319c291e40b915cc26d902069d03bd935553d4efa688bdbbf8@45.32.19.37:30303", - "enode://645a59b6e6e20ed8864767a1d0c731f89ae276ed4e04c4f10becce655532d95cbe1bc9e2da3f13a6564f9ca8fe46fab2781a380b3a89148bccac883d6068f684@45.77.159.123:30303", - "enode://7c2f43b2e7fded9469917311574d267427e62cd12e1864effd15f31c1246e4e955463d843acaa37309693a515df7986cb6d160b7e85a4ca2779a798a72d90850@108.61.194.191:30303", - "enode://5dd35866da95aea15211fb1f98684f6e8c4e355e6aa3cc17585680ed53fa164477b8c52cb6ca4b24ec4d80f3d48ff9212b53feb131d825c7945a3abaaf02d24d@178.79.189.58:60606", - "enode://6c585c18024eb902ca093278af73b04863ac904caabc39ac2920c23532307c572ad92afd828a990c980d272b1f26307f2409cc97aec3ff9fe866732cae49a8c2@144.217.163.224:31337", - "enode://edd90c4cc64528802ad52fd127d80b641ff80fd43fa5292fb111c8bd2914482dffee288fd1b0d26440c6b2c669b10a53cbcd37c895ba0d6194110e100a965b2d@188.166.179.159:30303", - "enode://d19783546438e8bfc11a35457ff1f47871a2ce4801b9b2dbe7606321a9ce29007305497eb8c98d7ae9dc5a913ee5533c3691b1080f7066697c4276e6140d2eac@45.77.47.184:30303", - "enode://13ed57615447bc9bf1da4e28249369babc00d2530d6c103c12350453e469a5e90cbcdb787c436977467f5864be6e64f2520180fc60b841c8c3daf84df9450190@104.207.152.17:30303", - "enode://59c228a6a0939a0b53edf6924bc7bd1384652dc1da0777495acd0707975f485f54a77c7b2dcbeece9340a43ccd9c7ea70f0cdfe48936537d238b50e5cb5dc0b2@45.77.233.0:30303", - "enode://9d960373335c1cc38ca696dea8f2893e2a071c8f21524f21e8aae22be032acc3b67797b1d21e866f9d832943ae7d9555b8466c6ab34f473d21e547114952df37@213.32.53.183:30303" - ], - "accounts": { - "0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, - "0000000000000000000000000000000000000002": { "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, - "0000000000000000000000000000000000000003": { "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, - "0000000000000000000000000000000000000004": { "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } } - } + "name": "Ellaism", + "dataDir": "ellaism", + "engine": { + "Ethash": { + "params": { + "minimumDifficulty": "0x020000", + "difficultyBoundDivisor": "0x0800", + "durationLimit": "0x0d", + "blockReward": "0x4563918244F40000", + "homesteadTransition": "0x0", + "bombDefuseTransition": "0x0", + "ecip1017EraRounds": 10000000, + "eip100bTransition": 2000000 + } + } + }, + "params": { + "gasLimitBoundDivisor": "0x0400", + "registrar": "0x3bb2bb5c6c9c9b7f4EF430b47Dc7e026310042ea", + "accountStartNonce": "0x00", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "networkID": "0x40", + "chainID": "0x40", + "eip150Transition": "0x0", + "eip160Transition": "0x0", + "eip161abcTransition": "0x7fffffffffffffff", + "eip161dTransition": "0x7fffffffffffffff", + "eip155Transition": "0x0", + "eip98Transition": "0x7fffffffffffff", + "eip86Transition": "0x7fffffffffffff", + "wasmActivationTransition": 2000000, + "eip140Transition": 2000000, + "eip211Transition": 2000000, + "eip214Transition": 2000000, + "eip658Transition": 2000000 + }, + "genesis": { + "seal": { + "ethereum": { + "nonce": "0x0000000000000040", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + }, + "difficulty": "0x40000000", + "author": "0x0000000000000000000000000000000000000000", + "timestamp": "0x00", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000", + "gasLimit": "0x1388" + }, + "nodes": [ + "enode://0d88e242aa0b01ee306ca43e956174677c96ec8eba4197f4d8be6fd7d4f2e57731e95d533b88229b66eb1a44399d870e99b7a4fe6547c8c80cdf00407a986e14@94.130.237.158:30303", + "enode://4be9e419d3efb0214faf3ef1794a0c33ebbd7633ece734a0a956faa166fefc496b2692a2a485adc66af805e461ba3e12f8d3941ec207e56bb9f3d3626787a705@94.130.237.158:60606", + "enode://834246cc2a7584df29ccdcf3b5366f118a0e291264980376769e809665a02c4caf0d68c43eecf8390dbeaf861823b05583807af0a62542a1f3f717046b958a76@45.77.106.33:30303", + "enode://d8059dcb137cb52b8960ca82613eeba1d121105572decd8f1d3ea22b09070645eeab548d2a3cd2914f206e1331c7870bd2bd5a231ebac6b3d4886ec3b8e627e5@173.212.216.105:30303", + "enode://9215ad77bd081e35013cb42a8ceadff9d8e94a78fcc680dff1752a54e7484badff0904e331c4b40a68be593782e55acfd800f076d22f9d2832e8483733ade149@213.14.82.125:30303", + "enode://5dd35866da95aea15211fb1f98684f6e8c4e355e6aa3cc17585680ed53fa164477b8c52cb6ca4b24ec4d80f3d48ff9212b53feb131d825c7945a3abaaf02d24d@178.79.189.58:60606", + "enode://6c585c18024eb902ca093278af73b04863ac904caabc39ac2920c23532307c572ad92afd828a990c980d272b1f26307f2409cc97aec3ff9fe866732cae49a8c2@144.217.163.224:31337", + "enode://edd90c4cc64528802ad52fd127d80b641ff80fd43fa5292fb111c8bd2914482dffee288fd1b0d26440c6b2c669b10a53cbcd37c895ba0d6194110e100a965b2d@188.166.179.159:30303", + "enode://9d960373335c1cc38ca696dea8f2893e2a071c8f21524f21e8aae22be032acc3b67797b1d21e866f9d832943ae7d9555b8466c6ab34f473d21e547114952df37@213.32.53.183:30303" + ], + "accounts": { + "0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, + "0000000000000000000000000000000000000002": { "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, + "0000000000000000000000000000000000000003": { "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, + "0000000000000000000000000000000000000004": { "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, + "0000000000000000000000000000000000000005": { "builtin": { "name": "modexp", "activate_at": 2000000, "pricing": { "modexp": { "divisor": 20 } } } }, + "0000000000000000000000000000000000000006": { "builtin": { "name": "alt_bn128_add", "activate_at": 2000000, "pricing": { "linear": { "base": 500, "word": 0 } } } }, + "0000000000000000000000000000000000000007": { "builtin": { "name": "alt_bn128_mul", "activate_at": 2000000, "pricing": { "linear": { "base": 40000, "word": 0 } } } }, + "0000000000000000000000000000000000000008": { "builtin": { "name": "alt_bn128_pairing", "activate_at": 2000000, "pricing": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 } } } } + } } diff --git a/ethcore/res/ethereum/expanse.json b/ethcore/res/ethereum/expanse.json index b9b734e313df34f740d9e1ca23b222d6a1dcc1a3..2061231c60de6385374c906828fb809474f3d23c 100644 --- a/ethcore/res/ethereum/expanse.json +++ b/ethcore/res/ethereum/expanse.json @@ -13,10 +13,6 @@ "difficultyHardforkTransition": "0x59d9", "difficultyHardforkBoundDivisor": "0x0200", "bombDefuseTransition": "0x30d40", - "eip150Transition": "0x927C0", - "eip160Transition": "0x927C0", - "eip161abcTransition": "0x927C0", - "eip161dTransition": "0x927C0", "eip100bTransition": "0xC3500", "metropolisDifficultyIncrementDivisor": "0x1E", "eip649Transition": "0xC3500", @@ -37,6 +33,10 @@ "forkBlock": "0xDBBA0", "forkCanonHash": "0x8e7bed51e24f5174090408664ac476b90b5e1199a947af7442f1ac88263fc8c7", "subprotocolName": "exp", + "eip150Transition": "0x927C0", + "eip160Transition": "0x927C0", + "eip161abcTransition": "0x927C0", + "eip161dTransition": "0x927C0", "eip98Transition": "0x7fffffffffffff", "eip86Transition": "0x7fffffffffffff", "eip155Transition": "0x927C0", diff --git a/ethcore/res/ethereum/foundation.json b/ethcore/res/ethereum/foundation.json index 53322e6ffecc1fb33c6c367ee3372ce606393998..1576d19f18ce135a411c5cb781ae92b3947bd93f 100644 --- a/ethcore/res/ethereum/foundation.json +++ b/ethcore/res/ethereum/foundation.json @@ -129,10 +129,6 @@ "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", "0x807640a13483f8ac783c557fcdf27be11ea4ac7a" ], - "eip150Transition": "0x259518", - "eip160Transition": 2675000, - "eip161abcTransition": 2675000, - "eip161dTransition": 2675000, "eip649Reward": "0x29A2241AF62C0000", "eip100bTransition": 4370000, "eip649Transition": 4370000 @@ -148,6 +144,10 @@ "networkID" : "0x1", "forkBlock": "0x1d4c00", "forkCanonHash": "0x4985f5ca3d2afbec36529aa96f74de3cc10a2a4a6c44f2157a57d2c6059a11bb", + "eip150Transition": "0x259518", + "eip160Transition": 2675000, + "eip161abcTransition": 2675000, + "eip161dTransition": 2675000, "eip155Transition": 2675000, "eip98Transition": "0x7fffffffffffff", "eip86Transition": "0x7fffffffffffff", @@ -174,8 +174,8 @@ "stateRoot": "0xd7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544" }, "hardcodedSync": { - "header": "f90219a061d694007fbaca6e23e73e29c8c6a60099abc740ab7e27ae3cd1b9c6a47efef7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347945a0b54d5dc17e0aadc383d2db43b0a0d3e029c4ca0a2f1bdabc1a72737de1c22a76cacc8fc162366904f759a99db7d6d19efee3090a0ac5f5b236e8977928a2ce43c7569ea5a74919643cb0b06d7540407b1ea1298f0a04356ddc5d77c83923a6541260308be167386e0969a608a017770c9e38091cfcab90100a00010002001009080011010141088000004000080081100000a002023000002204204801204084000c000010008000000000880080020c0000440200460000290005010c01c80800080004800100406003380000400402040000028084002a80087000008090a00200100544020019580022000000306100a0080100084020006809000e80000010000254810002000000a240050014200002002c10809202030006422022000203012000241089300080400000009001021020200012410348500028290230408100302000000058c0000020c08c20480081040020260004008481000080000800010010060020000e00020002140100a8988000004400201870b9af4a66df8038350a8018379d54483799eba845ab0426d984554482e45544846414e532e4f52472d3231323134313232a05eeccc80302d8fecca48a47be03654b5a41b5e5f296f271f910ebae631124f518890074810024c6c2b", - "totalDifficulty": "3144406426008470895785", + "header": "f90216a03b798fd7d7c51f61fdbe7a08d6d2257eea4501c12dfc5442146b85837c0da51fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794ea674fdde714fd979de3edf0f56aa9716b898ec8a0a2df7c321f2a532f63cdaf4e234227dd067f3782787db2ac892e875a8cb6842fa0c3a3f2c96c938633c7a99531a3876d544dbb8d5fac06879bea8e3cc5b6ece09da0221863f4a4fa6ca8dc7b6eed0eaefea36a5069f06ba9a61e78b87b634b5e4409b9010000a02080204012000000004100800000c0a08040041160000000740200200148000000000104011040800081808000102001180000000a80000011401020002c14008402000000100014400e20082080400000aa004100000000d10e8a0026180020882008200400a548000000201010088080000c0020800000001004046200600000052004020001800400800400420001800084002c1200040088028840004604020820400000264000005808500400410451c0808020140380c02014000440000002010422080800000240000000048a80072140000400409020020220810010020018008021800280a05008020000400000000044178000008000044410870b075e7ebe9d268353f001837a11f88379cd17845adfefa59565746865726d696e652d6177732d61736961312d32a0c92755fe5da24ea52d89783e387d88e1c2e24ac202641dd9906fc704a88979d28818d4dcc0009d0541", + "totalDifficulty": "3828700463149109414955", "CHTs": [ "0x0eb474b7721727204978e92e27d31cddff56471911e424a4c8271c35f9c982cc", "0xe10e94515fb5ffb7ffa9bf50db4a959b3f50c2ff75e0b8bd5f5e038749e52a11", @@ -2757,20 +2757,122 @@ "0x568f44291c13efc908db42d2473bc91ebb16e062e9b4368bcb770a3033d67741", "0xe5ecad510448855ff0aafb92a8c7aa54aca0fb390bec3c14ad5d2ba200380aec", "0xa40aa7655c1458b76c04ea5934ae171fb01c72c8c375bb61a0c27b0ebd70f21f", - "0x770ad5107ac47fd983979b016553ca8f340a13e2647b9140e65c980dcf349cff" + "0x770ad5107ac47fd983979b016553ca8f340a13e2647b9140e65c980dcf349cff", + "0xfd074cacab08d2d5f113669672632ab0e94e03bfbbe17fc3de27a30e9f0e8327", + "0x2063bf5ce8eed483242157113534b314296deb123cb33083cb0cce04db610c9a", + "0x9dfb8274e842fd3cb7d73506e64adacc39eb12ebd8a972b7101385ac4eb5b12c", + "0x8294c3d4892cb0bcba9acdd31d53af5bfc97daf124f08d903297da8bbb28cdfe", + "0x9dc2d04fe197009f24ebd4a0c95a0b91d4ff0093387b61569af993fa91edb3fa", + "0xe88e256abdc6447b1f141c2fcf2c46721d9717a9fcb302513c93ed6e31b7c11c", + "0xd2ad4ca6091f6ae6c323b6565522c208a839e36958a1675e5e9b48b13068e7c8", + "0xb168169f2643b8cdbec58f3f94d7889af37117a35389726563ac770f5205faab", + "0x09f32b423ca728b8fd04f9872325c0e2a4c9b1eb36fab49b31ae51254c69ebab", + "0x0b47f8f28a3242c9ca705fd11949e648f190053de023366f98e6c736eb2f0de1", + "0x870a8067623b35b5f0146ec95047869357617fe3773a8285a622c18d44963f9f", + "0xd14e05d8f3238294ffd8885c1f1710f64f2d8eb3d3b4e8a921f7f5c94a0a6288", + "0xf9cdef6ffacbea7692e9fef3de1b778bd11399492486dca0209b27bd474eb04a", + "0x5dc41201eaa00957d070c68bb289fc00eb31b7e5fff52f0603d52d27a40abf81", + "0xae3a53054ff4e3e5562795b45777e1e3e82abeed84e2fab748af50aace8bc8e2", + "0x93fd6f9af3b1efd27487477edccd690b19902300a4eebd34cb0d9b2e60b3cccf", + "0xbe475a9dc045e70ac8218e5a65450edf026d70f3f814162251f83a88098245c2", + "0x20566435d247cc1e7d0d81e143c01b6649fa1fd3cd5a9f97738e6045b29460c5", + "0xb37273228fe8b0c1679bb2dab7176ee1282400daddaaeb0db415c716f5dd1f71", + "0x494bb2cea59fc43629b0f8be3fba9ee5b52948c72fd1f1c94d31600756bfb675", + "0xc690eb3d8e65d5efc211def8fe8f198e42c0321000b8998a6ab218d897d593a5", + "0x26821d73690b9d1bdd9594c92b086291474cd365bd41f7de5edc49dfc29c26df", + "0x026a57937338c9e168d254f99dc9dbe3fb41508049ac1a5b6d7df868e2935e6c", + "0x50dc451812556bd69244f3728c6a17e958b2fa125248103942ac94142610bcc7", + "0x0ffe7ea32c3ef43049d8041a16bd3381ba89b1439dc5329e839546aa01a64726", + "0xab1e06f13dbaed341fc9e33ef387ebbe187787ccd8961a47560f7e02617a06db", + "0x8b72166b0d16ebfd9c3156f5972e3778e16b3d7edf3decd4b6efbc779990ca3c", + "0x149de0dcdbe03bf6d0ae9ccc989a074a4cf2e78311ce8cb6dee3856c2a374caa", + "0x42147f5960814dc31cc38b692089e6d1d53b85cc85a69e4cc01954dc61c268a6", + "0x1460ceccff1ac00f3e126383c3102c424b75c2abeb973d0baa8bec8e7bdda692", + "0xdbbbf28a5d25c832c5f625868dea79a481dce880d5145f4119d3e9eb32356a6b", + "0xd8e5b32a88bdb502fbd62888b9906a0c7955ccaaad1ead31636572a07fe7419b", + "0xdbc42abbec7d223666da11767d9862cd1458f91b5bf2f6499da4ae5bff281888", + "0xc3138499753f545de139238bdc8cd4cbad254c6b87bf0eb37dc1fe7a85168e79", + "0x42be700f627bb081543ab289fcd01235bedea5e33a9779d6d14744685f5a652a", + "0x8660e4130c98f6a8024c1b587dd24ebe402126d4d9a2adf610c62bf033b749c5", + "0xdab926bab8c4ff9e492f992b5696e8f7e3ba3ce77e49fe0e2ca36d3efc8791b6", + "0x0e3911bcf379fd57014bee6a1f796b884c62f2392cf6e8ac5a145126e4622b88", + "0xbd52346714664eeb7b4034bc6a54804824bdd40d9e5bdbae9375a302926babe6", + "0x641e58eaeb404f7a40b9c4be686c26721563b310f7624a654185a60374c1512e", + "0xfa07b0f7b970413ae5085f042224861bb1bc5f2f6ad72a44c3681d47a817c0a9", + "0x70cf3fffa7e27f03d5cbb1664bbd0e1315dfc43df76f1b0e3dce494255e54752", + "0x1d5b0118939994aa8be89dc365f909309df84e7c92530aef25d62118b09e0572", + "0x8b34c5a5cd5a42a58bbc60166f3f2a4be7d79057691c708e7a9639c21b6f57c7", + "0x7b149005e7f8378189c70c33c073d897cb07f62ba5ae319088b3be204beea8e4", + "0xf2a41e8feb5d294faca47dd1f7a90f66b7b616addcd179ae60c5f173cd44a85d", + "0xa2875c8e5a226f2cf69f07e19b13a628ef6a8662f3a5de4c9d666ca5ebddb001", + "0x4231ca8bdccd59526900a487608912a4fc0918a6c693250bd67787bc61a9d09b", + "0x2b06301a6372e0fca0da8a15d4e31a08522ecb91efbda58c3e495e2ab7f4fceb", + "0xd0e19f62b07275097b56e7f409cd52826a1dbc0be26eeaae20562420559153dc", + "0xa4ecf1ef04bcc95ba0ed9214e9118129ae5497c37db94dda284dd536b4dd75c9", + "0x3dc87ba482aaac6b59678c899bfb1da9b768e1385e0f8e8574852efeee2db8c0", + "0x07f8df3f6ec63d926c3a76b17ad1750a9ac6c62a8cdffa549440e651f6087178", + "0x96952f8ba7c83d640119868a030cb7397624ef322b2d153aa3843259da5f468c", + "0x08eca2fb4295f069f3fa62849b354a9e10b8a8e9b771cb8d2790b28b7b09d70c", + "0xfd5832f531c8819c0ce9d031434c33eb442578e81cf711e8ba23e9b21d096e13", + "0xfee9a8bae124565c963b65fa47dcf7f0c9b5ef7e778c2d99539b8a04efd04522", + "0xead692739a0723a73dd38b3aa02f93d72b3b6922c2520ea0b74981eaafe7f6a1", + "0x890dd8befeee6c7c7b4942b02a3bf1a9d1b9e1140ecd1c07de570f38b6c323b6", + "0x690343489ed214b9195db6181c42237b7a62fc935a18ca1a5dc883a2045ceae7", + "0x4bdfdc1bef8c19b89344230c0f48841abfeea788a4973c34afff4d2039ce4417", + "0x6ef6a94338d794400f95f8c0b6d5a40df8b56c31e9bd0037bdd7e41234bb32a1", + "0x00b7568bfdf2d4780b8650a49f707752a12fe9a88302622e68f264622877acdc", + "0x83e96e0b864df30b53ae08075acc83f785e9b8b2bc80e8976b1a954f995df012", + "0x664305b610d0b3167dd25210acba7eea291a37a224546a6f6d59aa7b71d16e5f", + "0x3d3a6401ab83eabc2af15a23b9c0e6d123f661b3599c6131d5923a106f562ac8", + "0x7a11833fbc8ce3d9883088649b930238625e6f6136dfa06a00daf8e646cd020c", + "0x112c33932097c4ec4e18f6308b2a6502cbf746605f1e404555dd07703d60ba2d", + "0x9e77cd028749d69caf352a95f75dee842c0d9323e3618afa2927b2aaaf7fc4a8", + "0x7fe0a62bfd04e263b4c67c5808f6231ad6faca7ff0f5bc0c02177034608d6991", + "0xea820d62b423b68da2f8ce345dbb56e2205fdfae1fce261d64acc959fb30cccd", + "0xd8f6367448dec55e8e2e26b5d304d92969abde131d402de313b1705c0bfd528f", + "0x4b97f3b4e0bff7cfe322e4f50176f6bfb379a706d42308ae75b2a46735762a95", + "0x7425ff9cea5e4869332ce12ef727fbcb004add827eb567424fd4c17717def390", + "0xc7dcd4fd005ae8d4d9e125727b54a64e900b3fee9cdc82fb5057c012178c8483", + "0x8ba88bfba201db1db716f762b5517369d3ae232ca045d2932fc27f6c47b15806", + "0xad2f63fa4fddd0cfc7f6cb01116d285e8aad4b0c51fa9445d39a4cb4949ed020", + "0x366874aec4ea26ece424f5402604ec7df9f40fc4cb9087cd3f45e764b1e36ebf", + "0x7f27eb75010b0d5da72794c129042511e24794b6c8491c1ff2e257dadcc7052d", + "0x27eadc596f6eaeb9247704c3339f7fe4e649f683fd815f9d71270caf5d9e38cd", + "0xba3f72ce8f45b1575554bd0c64feda3959c2a68f0300f021b67880b56f7152e2", + "0x50833c82dc63533f7ec918cd6d58ffbef4e2d597f354589b8eb66c9de0fc9697", + "0x30fc354a8893f683ef03bb024254574b550710f3c05496976cd39166a29e1c98", + "0x1b6b8d13fca6583d358b173c1a255c3db9efed8ad72084eadf10e29868a26fdc", + "0xb1b2a80122d1210160d8649d31422a1edc26b23e8499aa3a88b047dc52b42222", + "0x4e6c85a13cc0c739fbdf844a4f52c78979a84a86db728e4a5d2043ee3b2fcb3e", + "0xe98c28b49aa690aecfa43527acd3e5a010c8f90faf79ef5b4b479a715fe66a99", + "0x5b56e2b50ec96f7bda3f529ed71321650d688e2ee5c16723f6199256532ee660", + "0x1c9b8f5106a23096f2f57dfdb2a7a82876f4374be97b6a818fdc3c742b216b09", + "0xcb6973f775356ec7596ed6f9a036258e98677c7c5b2358da75af28f553327506", + "0x26b42a54252893f59e8eca923a0d2f2c0fe507a680e8907e2904702791c42aea", + "0x25fec26921bb5b6cd9657588b09813270cad72947abc6cb2726764db44181ff2", + "0xdfbd186df632b63f4cf74db660c59e658e0b4b725fe244a563c6698dd94adaf4", + "0xd1e00635e2399f6fa625f5c204e4c1643a7b619271394141433d26d902479bbe", + "0x3d3323fea45851eb0bc0b6c80a79cc0d0582abd8032aba957e719c31bb7901e6", + "0xe7d7b4c68d4f55ea8b71b43ad20bdd86402d36b6360ed0ca18082942736a7e41", + "0x1436749eca0a72b74bf8d596e3499414d412fbea0b936ee3564c4f83de907092", + "0xa828c16af52bd4adcb64928fada82a02d0a49fd3f53c5083ca9acfd998f8af1d", + "0x0fe559ad45cde755dd41876c5a65a690d32fc3d294fafeaa43e3fe133ae48da8", + "0x8f91d2082e5a1e6c1c905936515ab5f03889ac67261ef1d892fd909a9379a8ba", + "0x3881a9fe4ba6a24dcfa86351be2ca741dc9b2078409b2e7e0140569f19733505", + "0x4a3295525bfdda29bb9a552d8dc5c992649699f5694f96ff5bb0647910386db2", + "0x337389b3e800bae87fdbe5271f2167f169ffeb4710ecdcea30f08b2cefba57b1", + "0x2978e1e3c2b5dfe0b41ceb5c9c4029f42d346a2123a199c52ba6efdbf1d5fb68", + "0x8abbdb4f1f88fe8900afdfe15f3d03244435d4fb871606a689c11f6420145b45" ] }, "nodes": [ + "enode://81863f47e9bd652585d3f78b4b2ee07b93dad603fd9bc3c293e1244250725998adc88da0cef48f1de89b15ab92b15db8f43dc2b6fb8fbd86a6f217a1dd886701@193.70.55.37:30303", + "enode://4afb3a9137a88267c02651052cf6fb217931b8c78ee058bb86643542a4e2e0a8d24d47d871654e1b78a276c363f3c1bc89254a973b00adc359c9e9a48f140686@144.217.139.5:30303", + "enode://c16d390b32e6eb1c312849fe12601412313165df1a705757d671296f1ac8783c5cff09eab0118ac1f981d7148c85072f0f26407e5c68598f3ad49209fade404d@139.99.51.203:30303", + "enode://4faf867a2e5e740f9b874e7c7355afee58a2d1ace79f7b692f1d553a1134eddbeb5f9210dd14dc1b774a46fd5f063a8bc1fa90579e13d9d18d1f59bac4a4b16b@139.99.160.213:30303", "enode://6a868ced2dec399c53f730261173638a93a40214cf299ccf4d42a76e3fa54701db410669e8006347a4b3a74fa090bb35af0320e4bc8d04cf5b7f582b1db285f5@163.172.131.191:30303", "enode://66a483383882a518fcc59db6c017f9cd13c71261f13c8d7e67ed43adbbc82a932d88d2291f59be577e9425181fc08828dc916fdd053af935a9491edf9d6006ba@212.47.247.103:30303", "enode://cd6611461840543d5b9c56fbf088736154c699c43973b3a1a32390cf27106f87e58a818a606ccb05f3866de95a4fe860786fea71bf891ea95f234480d3022aa3@163.172.157.114:30303", - "enode://78b094cb27ceeecbe311bc278f4fde8b9a265db42d268c88484c94d7a2d19b82a1bd22dfd6c2bd4d90f9b05e6d42255e6eb85de15f73848ff82ed0be9cdf5202@52.233.198.218:30303", - "enode://00526537cb7e1aa6cf49714f0635fd0f608904d8d0693b949eea2dcdfdb0abbe4c794003a5fe57aa662d0a9215e8dfa4d2deb6ef0101c5e185e2617721813d43@40.65.122.44:30303", - "enode://4a456b4b6e6ee1f51389763e51b80fe04782c762445d96c32a96ebd34bd9178c1894924d5101123eacfd4f0fc4da25b5e1ee7f18832ac0bf4c6d6ac81442d698@40.71.6.49:3030", - "enode://68f85e7403976aa92318eff804cbe9bc988e0f5230d9d07ae4def030cbae16603262638e272d19875b7e5c54e296ba88ab6ec6e98face9e2537346c4dce78882@52.243.47.211:30303", - "enode://dc72806c3aa8fda207c8c018aba8d6cf143728b3628b6ded8d5e8cdeb8aa05cbd53f710ecd014c9a8f0d1e98f2874bff8afb15a229202f510a9c0258d1f6d109@159.203.210.80:30303", - "enode://5a62f19d35c0da8b576c9414568c728d4744e6e9d436c0f9db27456400011414f515871f13a6b8e0468534b5116cfe765d7630f680f1707a38467940a9f62511@45.55.33.62:30303", - "enode://605e04a43b1156966b3a3b66b980c87b7f18522f7f712035f84576016be909a2798a438b2b17b1a8c58db314d88539a77419ca4be36148c086900fba487c9d39@188.166.255.12:30303", "enode://1d1f7bcb159d308eb2f3d5e32dc5f8786d714ec696bb2f7e3d982f9bcd04c938c139432f13aadcaf5128304a8005e8606aebf5eebd9ec192a1471c13b5e31d49@138.201.223.35:30303", "enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@52.16.188.185:30303", "enode://3f1d12044546b76342d59d4a05532c14b85aa669704bfe1f864fe079415aa2c02d743e03218e57a33fb94523adb54032871a6c51b2cc5514cb7c7e35b3ed0a99@13.93.211.84:30303", @@ -2782,7 +2884,6 @@ "enode://1c7a64d76c0334b0418c004af2f67c50e36a3be60b5e4790bdac0439d21603469a85fad36f2473c9a80eb043ae60936df905fa28f1ff614c3e5dc34f15dcd2dc@40.118.3.223:30308", "enode://85c85d7143ae8bb96924f2b54f1b3e70d8c4d367af305325d30a61385a432f247d2c75c45c6b4a60335060d072d7f5b35dd1d4c45f76941f62a4f83b6e75daaf@40.118.3.223:30309", "enode://de471bccee3d042261d52e9bff31458daecc406142b401d4cd848f677479f73104b9fdeb090af9583d3391b7f10cb2ba9e26865dd5fca4fcdc0fb1e3b723c786@54.94.239.50:30303", - "enode://1118980bf48b0a3640bdba04e0fe78b1add18e1cd99bf22d53daac1fd9972ad650df52176e7c7d89d1114cfef2bc23a2959aa54998a46afcf7d91809f0855082@52.74.57.123:30303", "enode://4cd540b2c3292e17cff39922e864094bf8b0741fcc8c5dcea14957e389d7944c70278d872902e3d0345927f621547efa659013c400865485ab4bfa0c6596936f@138.201.144.135:30303", "enode://01f76fa0561eca2b9a7e224378dd854278735f1449793c46ad0c4e79e8775d080c21dcc455be391e90a98153c3b05dcc8935c8440de7b56fe6d67251e33f4e3c@51.15.42.252:30303", "enode://2c9059f05c352b29d559192fe6bca272d965c9f2290632a2cfda7f83da7d2634f3ec45ae3a72c54dd4204926fb8082dcf9686e0d7504257541c86fc8569bcf4b@163.172.171.38:30303", diff --git a/ethcore/res/ethereum/frontier_like_test.json b/ethcore/res/ethereum/frontier_like_test.json index 6d2ea3693df017294ba1b54e0897c24d2c21d270..7eac5acbd0aa06bc1172aa2f1b7081ac8b02b3a9 100644 --- a/ethcore/res/ethereum/frontier_like_test.json +++ b/ethcore/res/ethereum/frontier_like_test.json @@ -127,11 +127,7 @@ "0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97", "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", "0x807640a13483f8ac783c557fcdf27be11ea4ac7a" - ], - "eip150Transition": "0x7fffffffffffffff", - "eip160Transition": "0x7fffffffffffffff", - "eip161abcTransition": "0x7fffffffffffffff", - "eip161dTransition": "0x7fffffffffffffff" + ] } } }, @@ -142,6 +138,10 @@ "maximumExtraDataSize": "0x20", "minGasLimit": "0x1388", "networkID" : "0x1", + "eip150Transition": "0x7fffffffffffffff", + "eip160Transition": "0x7fffffffffffffff", + "eip161abcTransition": "0x7fffffffffffffff", + "eip161dTransition": "0x7fffffffffffffff", "eip98Transition": "0x7fffffffffffff", "eip86Transition": "0x7fffffffffffff", "eip155Transition": "0x7fffffffffffffff" diff --git a/ethcore/res/ethereum/frontier_test.json b/ethcore/res/ethereum/frontier_test.json index aae59cb071e4ae574f3b07a7bdfe9e5805eec0e4..7e52f6ecf978222ede61680a3460d5d9a0c9f067 100644 --- a/ethcore/res/ethereum/frontier_test.json +++ b/ethcore/res/ethereum/frontier_test.json @@ -7,11 +7,7 @@ "difficultyBoundDivisor": "0x0800", "durationLimit": "0x0d", "blockReward": "0x4563918244F40000", - "homesteadTransition": "0x7fffffffffffffff", - "eip150Transition": "0x7fffffffffffffff", - "eip160Transition": "0x7fffffffffffffff", - "eip161abcTransition": "0x7fffffffffffffff", - "eip161dTransition": "0x7fffffffffffffff" + "homesteadTransition": "0x7fffffffffffffff" } } }, @@ -22,6 +18,10 @@ "maximumExtraDataSize": "0x20", "minGasLimit": "0x1388", "networkID" : "0x1", + "eip150Transition": "0x7fffffffffffffff", + "eip160Transition": "0x7fffffffffffffff", + "eip161abcTransition": "0x7fffffffffffffff", + "eip161dTransition": "0x7fffffffffffffff", "eip98Transition": "0x7fffffffffffff", "eip86Transition": "0x7fffffffffffff", "eip155Transition": "0x7fffffffffffffff" diff --git a/ethcore/res/ethereum/homestead_test.json b/ethcore/res/ethereum/homestead_test.json index c6d49b5455604566fca593e6b09130e327246a27..817bf5ff57f1725493c0156f7831a14213028aad 100644 --- a/ethcore/res/ethereum/homestead_test.json +++ b/ethcore/res/ethereum/homestead_test.json @@ -7,11 +7,7 @@ "difficultyBoundDivisor": "0x0800", "durationLimit": "0x0d", "blockReward": "0x4563918244F40000", - "homesteadTransition": "0x0", - "eip150Transition": "0x7fffffffffffffff", - "eip160Transition": "0x7fffffffffffffff", - "eip161abcTransition": "0x7fffffffffffffff", - "eip161dTransition": "0x7fffffffffffffff" + "homesteadTransition": "0x0" } } }, @@ -24,7 +20,11 @@ "networkID" : "0x1", "eip98Transition": "0x7fffffffffffff", "eip86Transition": "0x7fffffffffffff", - "eip155Transition": "0x7fffffffffffffff" + "eip155Transition": "0x7fffffffffffffff", + "eip150Transition": "0x7fffffffffffffff", + "eip160Transition": "0x7fffffffffffffff", + "eip161abcTransition": "0x7fffffffffffffff", + "eip161dTransition": "0x7fffffffffffffff" }, "genesis": { "seal": { diff --git a/ethcore/res/ethereum/kovan.json b/ethcore/res/ethereum/kovan.json index 0d00478c599dd8c3c9b502813dc652e2d87023ba..02d22bb4196a67e04dac04ad89dded105de9db77 100644 --- a/ethcore/res/ethereum/kovan.json +++ b/ethcore/res/ethereum/kovan.json @@ -4,8 +4,8 @@ "engine": { "authorityRound": { "params": { - "stepDuration": "4", - "blockReward": "0x4563918244F40000", + "stepDuration": "4", + "blockReward": "0x4563918244F40000", "validators" : { "list": [ "0x00D6Cc1BA9cf89BD2e58009741f4F7325BAdc0ED", @@ -55,6 +55,3428 @@ "difficulty": "0x20000", "gasLimit": "0x5B8D80" }, + "hardcodedSync": { + "header": "f9023ea032ac0e3f2dcc2042b6b47cbe502d5c7dc39d27d147e3273e17fbdf7966518a69a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d493479400e4a10650e5a6d6001c38ff8e64f97016a1645ca0c97490dcc8dc3a85050fb7df999bd772c3ab40cababcf5df4ea7141a9a183353a002bc045bf48b7208ffc5764f48c35162f488bd1213c5e96b3b06c0dd3a32f24ea07041b8f6d8db9964497fcb512b3de8e71cb87d89ab7ef592caff47b444f30637b901000008000000004000000000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000200000000000020080000004000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000080000040020800000000000000000000800000000000000000000000000000000000000000000000000000008000002000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000008000200000001000400000000000000000000000000000010000000000000000000000010090fffffffffffffffffffffffffffffffe836ac001837a1200830a4245845ae02fd896d583010a008650617269747986312e32342e31826c698416b80bf6b841cfbb3c579ee8e631a9b2916de71778e0c4f477d1424ed65cc9ca7958899c4cab09721b8eada42b49c146c870291efe475bb8d88652810819dc03ebda2659f3f100", + "totalDifficulty": "2330161772435517944412054200548742154048251186", + "CHTs": [ + "0xdb9557458495268ddd69409fc1f66631ed5ff9bf6c479be6eabe5d83a460acac", + "0xd413800c22172be6e0b7a36348c90098955991f119ddad32c5b928e8db4deb02", + "0x7f2f84afc6caad6efe59a0fb9ff523bdd96efbb5bd22a0f67b3be8bfb8bb036a", + "0x7b7d86ae379af1c88f75a4b44d3097213b3461b9b7fd7333eac581537982dbb8", + "0x8525f5fb8149a1a64a83ec1915a78f3ce03a4a9afa4683544076f40f0ddbeb59", + "0xcc4a4b3e1bad427ad0d8b8ffd1c1c0789a08a67a09fdb0d1ffa4b1e71eb43aea", + "0xbaeb6b0b2fded26cf7f731603df0838397489bc12fdf8713a7cff865f009c5ec", + "0x3dbef76befeb39249e5c65d068fd97b8aea736714001a6dc2353b46d44f4bf35", + "0x306d007ef62b7cfed18e711d1cf449c009fbd5a8af7c9af66f3cda47678b478e", + "0x6b6038335d65726218d1d0301e12e792488a7ea879367c95263a20870ac91aa6", + "0x85ab7fde05c7cf468c4a65c5f7817e902f16038b3ce5e359490acae9c7a4f6b8", + "0x6784db2e15b6dffe0db864b2738804cf7b9804ae8932a2bb93a6bd2f2d4a8c2b", + "0x634119343bbd64c312a540e290b227adf6a84872ca9ea0fb7b3a7f5c2a4ba293", + "0x5836715c071dcaed0a79c77c12a02d54e6af55baed2c9325e10debc6fb07726b", + "0x7508b06556911cddfe4fc80e8a24603fd93cc79081759d4b7f19b2a6550757be", + "0x85de35d1995609af96aab65b1509dae77281a72932cda1e52e6af1d2c81cd46b", + "0x6575f31d4a44a479af6e4f53b7f0a5a1156c6965767b6e06cc3da8b46fbf0355", + "0x1d2ca9c1629b0e0775756536de5afef7dba628856e87dc84e5e6d16b08e12bf1", + "0x776edcae4d9a2e2a461fdf99eaeb1e717e3599f77934e058094b769c0a51f0b6", + "0x7fdbd14ca3a7408577c4e5b3b982edec1e7a95db784a34f9828ef5a30878f85b", + "0x64bdaef77b520a11c7ba92f494caade113a7682a565126cc4231d825412cee6d", + "0xc413f58e336df38771b3616ff1c9a1cbc881e4729dfa0a4bcd18dfca8e22f2d3", + "0x8da02b95474762995773eea74254861bf3eb62c099488aea97d0d271677a513a", + "0xf8d0c469352bd199cf28c997530afc31eb6b47662e1de2a1af05bcf31e6d5d00", + "0x88d9d2f7c1c44d4d0498c1f5ec21ea5d2ed2987e62f8dc6b17359b40200dfff0", + "0xe389fde4b8b06ababced10fb3756ce80b01a36751defff9ee0528accbf979b0b", + "0xb1f3785c2d15132fe17cfd455d015a01628ccf0116006d57de6425ef32bcc63e", + "0x9b2b9e6c015d85d3ba14bdf6ba6889db43cb8dd02dd020d1a01a4e7831604f03", + "0x8b22134cbd6b94ed1d17c89dc73771a3d3df57bc5c4b3d1540631c18dada7e4f", + "0x0a08bcb5fc682d91f0f95f934b4a38a71afe763b4924c8d049e407dea2bbc467", + "0x1d67c81b3c9316621088b42599abaf7e7cce42d60da44d2eafc8b904413b2c77", + "0x7bbbc5f13015a3ddfed8d3abc1d85ddd4646af588b534656256d894677c08c0c", + "0x2a442700726b19f5c82f756b08d07e9bc2777fe1557c079cce7a391684fdb59f", + "0x94a08a1519a9261f664c972e0c56b4628c3a0ab25a245abb4957cddc8a8f4754", + "0x5982827a354ee3703427518938a9b5b281249c04265db359bc47a7061f994a3c", + "0x3dd33939d000789b3ae1221b47d80ab4b8b248f79c97ea69c10148a4f8e946b6", + "0x37e5bf322012483f8e702f8a2a511b5f1225293b7c1be743cc83c637ec83c5f5", + "0xe671341e7a0275c306d55183e36b06665d2380bec4865c4c7cba51612446cef7", + "0x57ace89c46a589ca5150fc99028f9d586c4a8b1b6cf7af8b17ec1a16ebbdf8df", + "0x633ea492c2391b957bdac23f5a9428a5ce69882dc90db968e2882304bdf2b560", + "0x16aaa8ad98f3d2c127ca4dee2c9e1a36a0d4009447240e3297484d7cb2d5ce73", + "0x21abe93df9c382e5897599f54a31d3f46ce8e5e969c655c61da1125e6abe45a4", + "0xb01895100c1b028bfaeb1a126c4b4208075ec2a15fbc29a6499edbf939e7866a", + "0xd635cec21335fe1a644ec8913ea79ea987b815964ff883f00fc01c3a18c9d7a6", + "0xe5c761d35900479ea482e7804a056ca810043389f96ad208f55282e2e7617000", + "0xfa73e547f6a32b8154abe367a0138219514bd0d3fc304c742cb8b3d1eb8f70c3", + "0xf9de607cdd47e7337c815f4fbd44d983da6e38d653c3d3fbf2c6ac8d33707a63", + "0xa0d2cde0aec685c1d6cc05b21bf59817f123700e778cfe5eb80eb11add7e3dbf", + "0xaaaa3bd9f11785b477762ea0a3fcbc983c8a11b34d64475d16bc8c7d43904b9b", + "0x0031a12ba8c6e7408c7ce7987179b7534f1aea95a10aacbba0dade542ef4e1b9", + "0x101544e606fc49a7555855b72be050aa8a7a77e1e8985c3f54dec8ff3bb49a83", + "0x62202e7ed22ddc5d0cb3ccdf81770d4ed935e771be3d689c4dfd24afdb9c20e0", + "0x312cfe1c896a2624e9b2096ef5f3579b543d76ae166cb468e76a3b3b7fccaec7", + "0xcf1820f096d4b80808c50fbbf8de1b57da7b399a6f40312a293c4b5c7eb6d73f", + "0x1dde45a5e8ee62605753259a78e2c34a74cd5cf053a4c045ec5467f9e1311161", + "0x6b6ee017250b468ef3ded726892be7e8dae77c9e9e998b7e9889a34ff5da0cfd", + "0xb82bad6b19786930dd7159f35a4a24336d3f3b5b905b43f2a58165e0845b881c", + "0x77d20bc08ac7a25e115a1d23157c35c6ec22afd89948daba7ebd4809c3b95a2d", + "0xbe24e34aeb20602c184302bb10ac1f424226a985741f34faba4f346d8937e737", + "0x505e5b92245ecfde4a870197f7ece1ad181b6e8be4efce08763a37947122f4ce", + "0x6aeb3807dfe7a6db67278b5b6b6ee9ebb8996034c5ec82b3446a8c2efd3588fb", + "0x495f38afce7862a6614ed3d35531994e8d1e81dede4a4c9702922a3c06084e20", + "0x4acef831186426af96b90e1007561bffe2e8074bb33955dc36cdffb85e61174b", + "0xb2d7638804d6ee3e0747a4de335a26506898cdfd9221fe7e7e464feb268136b1", + "0x1aaa327ed400e0f6780fbaf818fc1f69c6cf407ecf80aa4f769ce42bffed7cdc", + "0xb955281446f41ea2330d07603803e148d5f1a2519f6233cad87dfd1ce4985259", + "0xec5f0c5f001e7897464a5570543060957fe20f88a905d3769615355c240e4859", + "0x18f872e3796787ef015a99bb3bc5ee4d31eeeec260213da70bd452f64de14c6b", + "0x9df2268daa9354a8264b5e7428d390a8d813266188df966fd58549dbb66898bf", + "0x6fdf62c4c161997e854846ed44e2096ea948c40efcdba2479896ba87fe484138", + "0x206bf3386af35a010906ea7a5263a71fa47fd317d23b57e448e49a36082916ff", + "0x4f627219247a8a93fbaa32b7f95e84b45146752d245e54299e7f74ca7070b2a8", + "0x3235819426f3a8b7affa6b38c0627c8efe0c2abd1eac7f8d6545e4332ebbf4f7", + "0xb5e514ee180f781ffadbfacc1831926f24cb305c352bb76ed7ae0a8247e792cf", + "0x6f287758e18c7967a5d52c05f8458e2d3ab2ec86fe0ef1d3146b99d7c3e3f5b7", + "0xc17dee632a1da285bc78d6c83715810e0e003c65c61c4105899b440487bfd126", + "0x173bab1413b65b801e136fe2dbf335b7d1f8689a127eb549bcd24f55b0bfd979", + "0x7e85be26cca16f2f34468f1aadd30a4766e4fe9d3746455f301f7f6e1baedc6e", + "0x85a0a9be1ab79a754c8875688b19a12719087e09d4cbe03f29965bbffee8437c", + "0x9c1c826a39e48b1b8d9d88ed29ca2d1864425d28e273695e35168eb01179ee62", + "0xf88831327094e7ec631fb9cf42719f89c4bceefbf25130784f09f4fa0a3b0a05", + "0x813e01a9713693c11c2781ac98604e51d7c7f610effdac795f74d538f7e5ec89", + "0x35ad80a70b0086c0a762ec29c577b031ea85132f777a324410c3870128aa91b3", + "0x6db9c2349d95bd51dfcb499ff26e90bf73e999f25b693ffa232c573f67164104", + "0xcff82a9fddf588d3d40e642f1eba1b9d24ece27131104e2e3995d6643e8afbf7", + "0xc6bffabb187ad144e33e7e3867f94f754e7e479c503d03de4fa891d294b175e6", + "0xd360ab92cf13a519f5bb41ea825e9baac0bfdf2298220c5ea60304ee9ac15617", + "0x8920024d447d0635cbe88f642966da52c1ef74d3ddc6c1c2df1df80c0a0014a0", + "0xc64a9294f5ca5fd1e1e05403e625222568c32bfd2f2f95bed2ad532b4c3a33f7", + "0x258ec76b8532f1f5169b44d8f5e0c1ddfc19fafab4cfce7c6afddc8aa4978cab", + "0x47f2aac68456bc931a0a8c458b802651173b1ebda2e032fb9541139352148156", + "0xa87a36800ed7e27bbee7ad937c1ace3ae08e2ae50a6673af70a2ec6e44ef1ec5", + "0x9182c6debf58629bd0d37043cc70b3c5e4af18b8aaa23cc5b455f009d1686371", + "0x0fe5abb9ff345ca06fa55cdfaa0804a6523c3ae162638e95edee4ba092eec443", + "0x24d7d4eb0c17869860d83e8be8f7579212cab60655aea5fcf070cd1461191357", + "0xf0971d8a37bc9b6a54d37ac7cd90aa4766cfe78cf8a6f376dac2304cea05683d", + "0xdb3e2e62d2da694788d2a0aecb2226c01de2b9d655421a079e2bb34092e7fd8b", + "0x57100a2ff274daa465da031c86f9b282fa03724c93823399c08d6b014054d207", + "0xc81fd4efe86a9ad97415928ae213959f2a716361da4a2937ceaab7358f105382", + "0x5836a5c844e45ae687b388603d62d28d81cc98ee1eeea6a34dad79d7ea6a599c", + "0xc98ad6c12f3f371a69eccb37407b43d3185b68380dcefae448b2e43db41afeaa", + "0x33ea1db64fad9787532d20f7739dd53a35fa810db902f2c33dfec4d652921073", + "0x3219209ffb0a9c5ae33779564dbf8a9618fabd1c0830d32a06dfd5a3990f7dad", + "0xeb6a367ee7f10c5d2882d86850f4a2680f78ce9374b7218d7cff11f0b01ff336", + "0x026bb678fb224a4e62c0d31450cf5394f4f71b9519106a5e92d58464edfaf63f", + "0x040ae0a25cc2c70fa710d394be7212b81128d7593a5c210549a96bf391a2f58d", + "0xa32caa1d1b36281e2c917f1951b3b0a0acde7c78f775681dd09c7cb81a2f14be", + "0xfe532be3ae441c5e9ec98fa0e006fe8529dad0ae6ba384300eb2198841014391", + "0x49d3b4aa6742d68b59eea7b44aaeb0693670c7b3b43788e93cd99f10d7ddbb7c", + "0xb205da5396369be6b68fa19264df3326c2bc825d88814843ac4f654cc7f921a7", + "0x57521341b29c1ed0cb02e8a64c43155e6020c4d8cda103ff56b2d2bb0cacc749", + "0xbbfc055f310e3930447bd33092081917d7661056ded31d3828b9a4eaff3649ce", + "0x52fefff36b4a0a9c2ce8cda94cb792a8260507df759ae2348ce79a6df3a2aa17", + "0xc4a7420741c5e8e041b8c7765b5c48ab0ee01fdf780efc95f820fbf549e5331e", + "0xbf90dab5cfd58fe4899475a911afd460ed0bed6e79487d9739399f7749520642", + "0x1f27056f98106e007efed121b017e82fd5c9b2c05de65cfb28fa9f67edaa6eee", + "0x6ebc2d4f7bc27d116b499dcbbaef72f8dd053559dce743706ecae5ce5338b508", + "0xdc6eb02678ccd7b2db8a9a9272ee53fc8110415ae5e84fbb8bf981eb25ccda51", + "0x3e261edddd8d34ba379cf163dab618aabfc198ce81bcdb1484b71a5bc4b50fc5", + "0x128bdd0f27a6d741399010b26f4056f0a919e7c1bdebb1bb69f22d32c80a2b88", + "0x68a749488b6e5bbeef10c98b678f67d1a7e53fb62bf502dc3743bf3c9138247a", + "0x52dc462248ddfead4b4aa85f4b2179280799b902519e83f721a849cc2f2b6037", + "0x7d70e4fab02e6a462e4688e96f7fc56a4fb9a945da942925ae039c6f13907c29", + "0xc746bf3861b3b63bd15b3cf0f0b6c0c331da578c35705176b90dee8a5b1e0d6c", + "0x4e251d7c4b2ae114b2f11a532fa060f6f7b1c4f0bc760034fe4e69c053a43746", + "0xe67185a040a731762897ac2e07ec2b955644d0784d18c6351c805d28e83f776b", + "0xa8ed7fd02d1dca89683183877cdc2a4c9985b48141d7967b10b38b59b831cc6f", + "0xc1020b2e8587f3c967ea138ea755437d2660e69abe8caa0d9bf9da9201b50750", + "0xd789a8a4a025f5725759e359859f2e15f7901a077d7683e3572d9ddba15ae94c", + "0x54f4372c804a66eb887614df08364d0e6b1f10c090584619c89c43962f513eda", + "0x3aa00d2431ba1c4c03452536047bdad3a5af547d2e16baad0bfeea0e7a8c3722", + "0xb341d41f92def7f5bf2d7c9a39b9fcdb2892b95a63ffd7b643bffaa523783dbf", + "0x9796b217efb64cc5f829a668989bb1915206e016c9714325f068b8cb2cd303c7", + "0x5deef3457cc95fae69c47f35422ca8731c80da50fd7f664583d13bb44d2f9ac4", + "0xa381a97eb8cf01a9b162666aaf9dd3142c6b2a2247830faf693e894ce99809de", + "0x7418829d2890fc2514b6e4da7b5643af269016ce2dd31d1aa7333c66c1273a6b", + "0x65466713071b4b5abe6a9c9b0e9fab247d56d72fea481e96b65999e2a085de95", + "0x0f97a6523532b4b80a7b54c1850ac2b062ff99d0c7848457fc3465d0d60623dd", + "0xdd1826c79f92c089522f853f87b551104b8b173f1c1797a752f2e8da2fd506f0", + "0x86a635a38b956903ce95ed3af65257383e2d7d86e6b63a61deec3ea5c8acc081", + "0x4afbdcdbe4a647f1137d417ac3580a116022c87cd6e7511330c4f2494cd90a0e", + "0x1d179aa8b0cb3b27844da4f06734f199fca43b66319cd2f16a60722eda994121", + "0xb1f1e42972a0b99268f42f6149452019259565cfeecc3459a58c17ba69ec4ad1", + "0xb80f8b8bee43d9e98eba909691ed6c9e900e8757326f7d20b6e7ba741d404a4a", + "0xadfea6390b526ec42aec881cf8226b9ad94f28ca1e682237914b30564d887208", + "0x50619f2ddec37c184798dfe50b067ae4bc0401bcfc1ab4d42cc19627b7b183a6", + "0xa53a92d3d223652e6572a48ad7624aeb2bb24c10872d17ebefd41ead1579edb7", + "0xeef7725747b978d578e5b576bc18d5c6e7148b68c2389478e0102b7656e7f3e6", + "0xbda668b4961ca5f6951f6ac55ca3c03a66e475e21905dfa8e797cd4a5ef1381e", + "0xba8c3be48f94186114afaab72a27a21db0316856619b400645469511b2bcc165", + "0x44d80fc17e0d131e6e98a4567885cdfa5224aeba8616d8f369cc84f3b85566e1", + "0xdce65f6c57bb81f01fe80daa1c6869c890a4bdfaf0f016456098bbd13c376f72", + "0x0b63a4585671669622a3c751d5ff5433f07cb0e9f4dc0acd9dff493c6ed01495", + "0xa287abbc612e2d318488a370e792845ff01ba5f7d387a2f0d82c21fc3d236992", + "0xae83dd70c6cc9cbd2a418397401461687bfeb8fa6d606fb7105153e50b5dfce0", + "0x02e31ae89b0dd8f66b6590a1f65711145bb9154825b7c68ae5d76228d92f016b", + "0x1e38ed1613141cd046a2449540f1450ffdca2c1a8d9ec2f1ce8d9c74ee189d89", + "0xc8f4747dc7b311c1a234f7d7eff3e9000e11c38624c23519b889543d8d33dfb3", + "0xbb7aa989e7d116e3c288ed8fdc0c7884fb3c6183ced8a0ea63a9de037ebf49ee", + "0x88d5a00fda80c28fa6e6d0bf6a6891b492cd6c85526955cfdfbba2428c9353c0", + "0xab03bd8ecad4f1afd0ee84a2caee745d4b7ade0f32bfbd61db50be15688e4493", + "0xb9ee61e8ab9ad32da0728394b5ec53452014f6fd99a5409923cf75c8d739da5d", + "0xa4c357be81081b833b5d4fd3cf4644b2ef1a28fdf67a8e0b0b2af3e8cd6ade4e", + "0xfb56d0c3b65882317ba962e2953a75d9d9b43c9f40ea0894629dc09bb29f6712", + "0x0f354b29bf19612285957523fc7d487922795d8fe5fb0aa4019b94a1f13d89d7", + "0x61c6ec536ebec0a740ad39900a2a71cfae8e8ff81ce3a37c5a4354ae48d39ada", + "0x769d94d0b3fcdcf22eb6c36a7c5b18711c79f69c27e9a396476d03e92ab0d058", + "0x5009c84cc24149c0134790682e0a9bc88fa5a8d2bd650207309002c25bc345a4", + "0x32b697d3cf2ab3bfb9ef79d27f3e61eb423ec9d802f2256b1d8875df78ca5887", + "0x3b8ba94c607acdcaaa7f0f7ad30028ecaf4d505d0675df95c358607210a23eae", + "0x433ea023715de94c5885d16762c0caf40079f5377f3cf3f5ee871c6812182ebe", + "0x4758426d43b67ad4d6e7a36272a3d772b94dc497d45292b061ac655111c41240", + "0x12542964fe7e8c837cd8e20a0f80de473127bab6aecfcc5be266bc7bcee87ccf", + "0xbe7362114d69220ba08ce42396e92d87027a63c3287be0fcf633c8a2853b8c71", + "0x722e26ddb7fbcff7dec266ac0ca388353c3a34d8ee23bbaef579b7acda48241a", + "0x04ede2fbedd140490e0b215e89850b8bccb059cfd7a72bd2fc3118495aa4e811", + "0xf47ea26f501cc95c62c4da4c57e743bb20c4caf6e81b394ceea58bc64b1a77a6", + "0x6f208393c290c02f4a505a6e7b2420da859e7ee83583e9a41d709333e67dcf9f", + "0xbedadad9293e12d706c963f31fc06535dd23432f7b27d157e529cf73e4f5603a", + "0xbc3572c828c9c1d1f0c90f56bcee1fe414a413fdbb7362a416bf4033405276ee", + "0x084bdee3faf8a9e45990094d668f6ae0050c1427e3aa918223849357f7865633", + "0xec5a32f5ff6f3887e5aa33d6dd61d2d63e36520ff0647a9eff2355a7311a0fe5", + "0x13fd23c260c0298286e193f23295b67497fb00c60047017bc62f94f6a25d4574", + "0xda66b3d4773530c3a3b563a8721523dd10f4b598b3976aa2f3becbc15fdda495", + "0xfa981ea461800e9eedd73c1423291c69bb43555321e1c5710a052a12bdc68707", + "0x232b0ab7709616262fb48d32ff36284ceb5dafc823ff4f6d5b79e8ed12c7040a", + "0xd90bc0dc26f812e0f05433c30fd8e19e81a81ac589c49a528e86027e8a352e78", + "0x3666c6eaef02990c263e0611e90d793c82815bc0032c2ae16813e957659c6412", + "0x5b46e134edbbc9466540878a637ce22194f040f519efd98a52da8e9bff05595b", + "0x4bff68f29ddbf33950af965ccc3fad46c81b6e2371922abf27b88fb2d669b83e", + "0x67dbb166d4afd3d1c32b9ce9ed5da75a25eaa160f90baf0d46a74fb7ddb09dd2", + "0xdba720e169d3c1dfde88d08bdf445eb428908560a520ccaa1da84d2dc07369b7", + "0xeee7477387db069e7de5d10a8e77d7f3ac7d692b4d582b5256bac44ba3d8381e", + "0x968ffed5244efe79aa4dbe1bcc7a05e24b5e50d1fd4d92117e1b654707bd3c43", + "0xcff2abc22b0cb5865f9767b5f6fa4b1420e3b774ff4de6f593516db3511d9fe6", + "0xd710ff4e765bd0d5e2284c8a11726c4924b6ba159702214a6016555110c2a251", + "0x31523dcadfbc24f1d3011cc1cff03a4df5f2c10fc4bef7cf0bb807edd1c97520", + "0x079cf6974e4e64675de7ce75b1262e2fed194440c2789f6fd72110f105503754", + "0xb121c3ac31a421442faf07a4b553de22be8e1726a67b0feaa4d426e5b48930c5", + "0xc39a8aa81b5c0a95d0037e20f3043e16832cd4b4cfa78e59a424493cd8322754", + "0xcb30c5710457eb0d9ece9274fcf01a96758e6e48db64093b1a08f773f5bdd594", + "0x916203fb9c6cb117f46f66a6693501711481691c0bef0ba0563369841762c657", + "0xf6711b05e901583fe0b51177597d44354ce57364282e042e143f37185af0b13e", + "0x3f22f516c9fb33c327d2184b842edc2535dd6cf4969178c56ab98ac11996ea46", + "0xd51b6361e37ceb481bc6e37eb61432fdfe23d52f2b64aea70c0a12a3c79d2784", + "0x9a1e99c578e7f7a69b7d914c0f748b3f94b6b30bae1be056849bfecc74e31751", + "0x5539c971cb882506860bf724b3f3d05cc399af4640775ed805288468dd83cfd7", + "0xc54c50d46acb1fbe017364ebcb66b4563bc0ab60753b055a19b126a55c476bc7", + "0x3adb55f5b09858cb849ec9edc3adf5e3ffb8eac43434c1ccb817038610b57c08", + "0x5fa06980ab83374613079b9d61c53d6ba93d41af996f5d9fb5e5ef05d36d8aae", + "0x1b66a155b1ebb780d501ac9f6d14f8ce6c644af88b7f86f87b5815379258c494", + "0xb75860039c1886823bd489d9f3b92c4f6bc46629d3fedc9f7d6fde59a318518d", + "0xaffab6d2682c1641658270a5b63e9e46b0bb90c05eeded11079b8f7d33899983", + "0xb5aede6209f50349dcbdaf85ff444e2252bd3e8e2faac12b517552b493ce4cde", + "0xa7c3e97ca8efbde3c829f2e363c5aabde3afe2fde9a1d106f868e87c0fc0682f", + "0x84f7405932b9aabbda33ac93170ffb73ccd5c584f0f55c17af4cd66fc938f739", + "0x7eb2a3d7ef2dd3fc0fc337070f1429e9b472a5912f9217ce4777bbdd07911034", + "0xb208f1088e65776f684d8b097b847c98cee4e274d49b07918e4ebae9585c443e", + "0xe0cec73204567174e0594f4082d01d7b8e8d382a6602e40b35b3197ba2eead6b", + "0x06642dd13587031239255686a40625c53358c4ced6eab78c696413f3cad9c6d8", + "0xb91a9a676da327548f5ed89ca74608c9403b994ed8f0cefeb53a1b5b09e76da5", + "0xe7dec1d92e8d551a54e7e310ca217933375c3a3944371ad5975d1601d5c6d0ac", + "0xee0d0b46a607e2b7df37f24ee652dde4ff65c95eb94b93092452a56f6ef9c754", + "0x8ef8f5852fd8e1f96f82cd3f4bdc716824070b3eaccbc2cca5da345298b752e4", + "0xe801666d13de5a51eba7c9e1d149339023b1b56c513bcba2e37dcf22bdb261a2", + "0x37a53559f626a819de48d74f7e6b484ec90c1fac766adfe08bf9234a139b0fec", + "0xeb8d703867e169bb31f5390ec877a7c6c82aab53f144593b75fc1599604d6c80", + "0xfed3012f3f281de26b447003799eca69e9845c6b5be85bc42670a64487c92a3f", + "0xf70358dc2c4e9d449727affd9cf45ddefa0f3480d964f85aaeb1543427e062a5", + "0x6491b2c05e217b7f21094e96c3381164f9db86c480b0970a27d8e25c875f5b7a", + "0xb4561f6329dd718c03d7ef6be0224612d776b72511294d29b293eb2e0a7b5cad", + "0x89f4a1b613ee34e1728899eeb294c48a21c28da7035d5d036cc46439fa42c965", + "0xe78f95f15c015a18e4598e33f06f6517c4455cbd2efd0dec1bead6080f050b35", + "0x7c64eb0c96598f099d0e9383eeabdc303002819b35223f744bf8a2bb80cbe394", + "0x98e5b4eaf42e390156a730d9e80606d80e2c0c9203e5511af1cea5361af63165", + "0xa481b0af82146b7aeb690456acab564fe356b2b8a560c4b5ab46817167bbed33", + "0xddac876def8620f40cc43a04c8d939fa12bb6d985223caf955e8174c46578bab", + "0x10a97a5a30805c28828d6e4eb4205905845d121149b7146e7373cdad4850ac88", + "0xe14ce88a794100c126707edb5a99922d394e004ce5fe178fbd02c8a34202d4b4", + "0xc3f18b1d11de7706063e2aeee7159589f64539fee39db233ce6983b24ffff076", + "0x6ae533d2bf3f35c6f1f256b9d1fdd863d5394956ded7a80b56ab1e12da78c93c", + "0x79ec4573d7df2f3b48ea706582d48d29a8d1483ae29577fd85b11c346a22e3a9", + "0x263652485fc477fc5407ca079eb494d4618943d01ffbc27ba3824fbc34e6bd63", + "0xcd414c2dfd20a303c9fd623d847e0387cb93cff53d006f335358894be35ac0b3", + "0x75d23d879b23aca1f2152147212506b0cc789c3e15fa8644a6c89a10ac926336", + "0xe166233fed0bfa647b84b95b4a04553087b4f42784334f5017dfd2a8e246f0d5", + "0xc48f212eef40b227aa429405e6c63d25fb35e9889a33007fd1afcc2bbb57a8df", + "0xfe6e67fe4057baa0701a7bc7f98f1b59e8c0ef03401e564d51b9fbbb49a5d11c", + "0xc31d73c0443dad07f6eaffa3171c77c8da87c54780d4f374128adb79fee4ed54", + "0xa0ea13b2425ec84fea7bb9f121f0aa72cfb00e36019e63fee991606246569281", + "0xce7e8e4515b0ebfcdd0afdb002f9017076de88e278b914c902bafca65a8b12fb", + "0xd14a72a5605124a8545dfc4d7419f00e85083d558fc662dc17b3bb289107da83", + "0x3e89c3740c294a0f54068cf769091b1a976fe4e60ab4fa48b9c38c034c08e9a8", + "0x2ee8355965ba13e91d71bde2a72347709c97cc33bcaf1ecc6952c860b07c60c4", + "0x63a87a93a0e410e53146a60a46ca870604f8301e221fb5b470402aa2dd5b9c54", + "0x0bbdfc34316855dfa828a58923acd7ebc556c88a43b655d32bd46ec6008d8351", + "0x4721db737d99128133928670bd229a0a7fd2889e00e222378fe5f663e35587f5", + "0x4f52db42c0f03f342883d78d387ec913a65f92961276b0e718d4b5fa268aaadf", + "0xf7d28588843f736c341b8572322d1ab397e0c5863ac9a30facec3eae0126f3b8", + "0x48940fceeebf3fca0496483b58228516b9c54404fddd31e573c951b12d017d2e", + "0x87542780e6dad24843883c420ef6fbcb2ebea31b073fe54cbdbd5ffb2c4b30e2", + "0x35bb6ee5defc7d7489b24e9e8659b7131ab9f7673960f3ab9bc9324d6034f3da", + "0x11719cf1a1d10bc3c4a48ab177ad65039ff09d1dbb34b1b7c15fa78b5506b5ce", + "0xd086c0fa8b0a1f7cabd9bcc5a1e36137f59b7ed6d91057defc6f62fbbce73956", + "0xeb461e410c324c9ef9a0d240fc5f153e1af9e88810f1563767022723bbac62bf", + "0x10a384a0f6c927bc9efc21ae1b2988e79268de24065b91065b582aff4e0cbcb2", + "0x7020503d8d86fe9bce39301207283c9f9bea9d7789d0f1488506a4073d482d6d", + "0x93657d3b8bd95e33b9b13c3a4130a623d4ed6d8eca8cfde3e6fc9e16b3f1f898", + "0x40727606bcdee8714dc15b349561d8b7d293ca4231227384c56f1a8fab986041", + "0x5ba216663e223675ed7da19a7bbcb08483f6871cf11d911c3ba992829e40ce2d", + "0xe9eabb151a2d735f0ca36ac9ed65c612276a21e28b1a63b5fe632dba27207969", + "0xed88ead1835a70d30368a33955de7c4736bbdc22ba95f04aff7097589887b2bc", + "0xca47f392632e4684e54fd50f5cb8e6983c8c2085d4df8550e3398c4e2bfb9bb7", + "0xeb8699fcad43fac0bb54ab553c4f29c7d1a3ea6079e9d5218a14b675477576d8", + "0x912ab118b513c06e618f29f9a7273e8035b4723b3a21521a91a43100486759d1", + "0xc89d31d9d5bf2fcdcfbf8ba8c61307656bb5673474ea605d1e5d70ab1d7ba43a", + "0x8fbcad4bb94b91cec5d9ac2534666f019c807f8889d37e3107fba28002c7dd1c", + "0x3f606a227cd3a38e2d5da4c611b9069c109e9c291d390e5c1bb3b427be68c5d7", + "0xa69c47f146ba22a6f53562f4ec3d13e1cc5499db587095dadb4a89f6456be664", + "0x36a9e5d14b3fbc061dc83eccc0e6dc3f2a56f0a56e88a987f09d89257c2d2ccd", + "0x9d77184cdeaec05bdde7df7005467af592a625bb0a470e5b071d6cc6537a24b8", + "0x3f9634b50a524ff350dbd4e764d3c01d848b44400485f792d4ce4e17a3afed80", + "0x142e648622d2f13dbfa4c0e58f91ec0073455a6b327a2a2273fef05ca9fa7553", + "0xa4348c80308a3e0b0b4d290152bd09b24d2420281ff3240d2b54c406ba2ac003", + "0x95e86927eca33fadf4d35feb4514ecb90babbcf2ab5c801533a4d36ea8ccc87a", + "0xbefc0239b823cfca21afc0f57c144226a8a72f6acf86a1152d8a80ccb27475e1", + "0x1bdc16bd7d21c2cf818f4d3f5f1cab4a7d256ebb8b32a8464c6b6ca111c9ab87", + "0x04aa15ecdc0277166a77953b4596e2558af4295a342330ecf9d21b6221f21a40", + "0xb432d2660f59648ef7929901a0ce38ff577b4162a69ec92609c8ca1cbea5f8e1", + "0x985a13bc2bafb56e5ffd1b0e582790eff30786bbacc6e28ca57017d398f1552a", + "0x57c5399f6606f02bd06c9470246948e3d482ff9dec5de270f6c06dcc49c8c8dd", + "0xee471268956c3947f22dc8a6077c850f10c1227f50cd24ab7a059b94f3496274", + "0x97cc4534898ab427418bad275c9c21c18d6f51cead840bd5b800e27e1bb1f76f", + "0x1370774538f3c6aff104060091a573c94269421e5347d9ee26c9c5ec90956174", + "0x7b29a2dc9f4e8c3d43d3ec93d01484205583cfc6c8fac71ed0dd6eeab3c7e987", + "0x2127f737690dac5f24595b5d0b96e869f750b732fd8f004860b5510376b154a7", + "0x196f4545946a3a4b9d5a1c2ce3813e55245cefcbbe424eb50dc7126a6f7ce294", + "0x89a6c2dd239f741797b47166d4a97d8a6147b26c7f61471a272035fa6a19ca04", + "0xd4cec04b0a1a8a2240cd2745e53e55dd560c3da0bef625e3472a03c60e5e1346", + "0x482aa6954d7bfb528abdd0d2a44ca7256adc9d2b5c885f67af0424add0f85039", + "0x16d73722fc18c4c5fe53ec609695dab204ea3a0ac8d52356acc56377495dc715", + "0xa2fc87f2deaf252bc56f50d67e5ea24cdd23fc3a0931c69e5c7f832c57e6cf5c", + "0x95702d0ce28a00a2c52cac13db300b535e6be5aab6f1acd8f7f8f3664d8f1942", + "0xd7423e6ac1d49688437234bfcaff2f8075a6237b35b719084c6f9257d6b095ea", + "0xc1efed370db76aa0e6e31680e91ae03788ffb343c13700ed30012102a727ab05", + "0xad108c165dfc00e6db95fe8fb28a51a21f471d16093f761af92dc3c1a76cde18", + "0x579ff43e25ed4183ab6638a5b3b8ca5ee06343b410a47abecb154fa98fa701db", + "0x58446a3cb729e43ef984f52bd3a1c0c5bc2986b355993dc04826987c9a4c2b0d", + "0x916b6e17ed534b2ed79c4139250ced98cf81a39ce19092ca7593b8046b010fe9", + "0x5a85afb15b437acc1958c1a35beb35ea25a4e4bb2856fe82f4d2c257ad92e4ec", + "0x74f15f12f6acbbc1ebe68f82e5832c2581660451eecec1d2369ca842c0384927", + "0xe3724b93bfba09e69c4d3991f5bb7560dfa58f264a2ecefc1fae2a933fd81c75", + "0x4651a72f64ecfaa2b51804f6c02f0e341d43163b408786df50569668d6b1afc5", + "0xd67008562a459791d2de81c71369507475fc3727fb46f68ded3bdf313ae3dbaf", + "0xcfd841df8cc01abfdb09b47afd98caaaba19b900c7d4249eac819f24ef7ae98b", + "0xedb8f1bb736b4b16985f41b97209730b6f99d981fe3ca0c614d138cc9f3651e6", + "0xf03d65be04a3b5011007b48aa3eeae54b39166013f8f0977fe185036a2302360", + "0xdbc7e5449a0af2b69668ef3f860dbaf2b0e0053eb5f96bd5879f59d1535aac78", + "0x44e3d860cf489a017a5a0c99b7fe2facf87c7bbe8c641bc2ac020bd45ae278f3", + "0x8f2d4fb90e6fb5d1465e5e5368b36d44d2081842b2c3b1ac3c2789cc8e2061e7", + "0xb47a9bfac379a6414b66c2b79ced91d87f130b0c2f86c67ca5295fa656547b24", + "0x8975e42bd19f5ff5a6336f835a53e0e7d8ec1288b9f59c266e1da463fff8f24e", + "0x52a573f0165af4f0a8bbd90c8ec2539ff430ae8bb9818c2cbc2c91f82ec056df", + "0xba647605f77311d053ff9c76ac71e88b6d1dd6b4dd5296eb0b7fe3e07eb43880", + "0x1c6d4757e48f80ac0a247bc7b2649239d0284b2580248a8b6706f04ff47c1f1c", + "0xfe42314e663a9196f7bc6b795bdf2763f49467c9b2183eb02c25b5c79e20fc44", + "0x15b6054f899df7793f03ab925c1d8df2f94d6221552b890af6de5d8a5b7948f5", + "0x694798b4238b93838948377161ef5165af4dfbda5f0f9d8d2d37c083c3718393", + "0x58724b920890120f1f90ce8bc3e9c6639042b3cfa78123bc9917b8854315e31e", + "0x21f057c8e538eee76a95659618bccefab9a7a5ffc955781141a3eef71f3b3423", + "0xa5fec657cc7ab1b633023791470bb2c5a7f1dbfb3ccc4e6442565a791754f319", + "0x67a17493b80cf75ab905ccee157746c7ed048a8406fdd94af1c6916cfcb29220", + "0x9d5132754a73e0ba316da2f824f5264e851526c6a75bccd153cd41f232e1ba42", + "0xe9f6b8e973183e7b89ab72479003bcf07cd0d141521c23e4e4269f58f51692ad", + "0xe52cf37c47ddd837146288364ba448980c24dda74c6a2560a99ece3174c94a57", + "0xefb69d2b602cd8b073314ae5af7d6200d23335f1e46980c205ba0bdd979d0ec1", + "0x3e2f6e13459a82666bfc366941b39506d84b1b6a1c94e4361d51d30cc1bf4218", + "0xac8562bd8210325b1dcc8cc3c4214a97d04ce795c3fd9049c7296f0d855045a8", + "0x22b62db26899a50ef8630a9aac5cef645512355901ba14e8f1a3468610473756", + "0x24f7a40ba3243eed13a4c2ad7fb273a1fd822a3d2b8ec729b0e5184c3f791e76", + "0x1ea72f2b13fbc945e3ca51bd681b2e12e4ead38f42622ac3be54a1b89c826baf", + "0x95b26a476c67aa211235b0b177964422d037768a1c162526a53e73b2777b85ed", + "0x1ed2cbd748843651a8ab0a31b256d4d3c6bdf3cbd3db7e73090a679756880a6f", + "0x20e857d94a0038a51313c8a67d30f7aed8b335e7cebdffe8795ee03e62e5443f", + "0x9b807ff518a22a7a40521537b137cc25904b8d2607d7d9ce1f0e50f2327801b3", + "0x35e573f1dc47520f16eb43240dcddb95d167eb8e59430c608f17303a9c86b639", + "0xa06a21571951ec87f6cba9dec2cc9f1e55b4b4d5d3e2e9415d30fbe50370af12", + "0x7961da55526e7a67feeac00e625a1ca93607b7bc4d4d68142ba791e46d333e78", + "0x5accacce3901feda7004414e0d514129bcbb14ea727f34f58fddf4b928774589", + "0x0d66a4004a235178a69941894ccadc2e91a843c849284db82e90a088c3d3c6bc", + "0x8255a1650988cee8c57e30a5a8fa159f71fc2f3e23f799e11f5bb31b09c54667", + "0x7d6936163556f21f56185718a6b7de5bec0375a01f4c88467820d8c66dd983db", + "0x3e3c08ff6cd5f69aa8f85e9a5d8c91462cc228a41e392b7a544e2efb6af87cd8", + "0x4f401bca18b8cfab219430fd6807aff86867ea52f12b45ec224fa9dc318dd789", + "0xe14e32f37af0204cc56fb10ae891cb09b0c27a116ad42b5946dd1815f445cfe2", + "0xae46c70ae8bd612e760b614689d48b34358bf504a2414b660224312622917e4a", + "0xe8870fb901d8234646d87be0eb0b15d46f55c8d3765beb631dbd06da88d50038", + "0x997b7b18cccdd31b611bc01767a3be138f5ffb2c92de11aa1fa486a578c94799", + "0xecc5dea6b6e3d233f7caaf17e07a5f58febf9fa6d0736228da43b28cb5538dc0", + "0xb56b9b982a7915d85fef49df531e0a1e8e8d000b30da66104265eaa7317ee2cd", + "0x09601975476271e885e83f96799f15cfa6c0a67037b9d31eebf01cf510fdd752", + "0xab2e7c3c19f0d7aad30ff986e9b2c8a56f09906b851c53455af4d35d61b20a32", + "0xe5590d245bcc42d8efe40d677c42d840da1b32c49369bf352f46690bed9a4dfc", + "0xb46810ae0d11af6412e219bb99be0ac22c033433b1567a36a69cd962c9456d41", + "0xc296086ad6481ed8954c5542e4083388a4048132a952f500b2c362a19e8333c8", + "0xc39bf0d7e42ecfe35ab387932eb54ce0c1e502168ba5aa74b838aa17ec294d27", + "0x1ea48989819dcdf6aa0e9b0cc41d52a4d348fa80c8aaadb24091c517e3fd203b", + "0x58974a726103f506b2d8c904a8b942e247ca648678b7b98e484c203e4bf03ae8", + "0x0ccee08094a04e4db707f8d570c6098cb87b271a2aeeed08e0f589d2c162afa2", + "0x5a8620a1f85f856a8cfc105a05d4a935cfe626de46eac68e2c78c7b8fbb08c31", + "0x070101ef142836edb475e9e5bddfc821fbac8b2f8605ad9f52a1ae00bd0d1f6a", + "0x822efecd485d634912a4fdad41d72479c31b7cfaf664f3a0f78dc9ebb624cede", + "0xc6c7f14ff1b5618cfa096080c6fa59d301b3c2f516ed9f4eb0c3acdcac3e1ee2", + "0xbe85e53c24900f6d22564f33f05f1250c9f80b1b4616c0dcbb6f500f22d376b5", + "0xa84385d0d3f54995864695c96c5fdd30affe8dd9548b754edd12d80802706ef4", + "0xe12c3324e68b4efbb14fa5522ef6d2418ef7c397f390c2b91e30352ffb538c48", + "0x06cafdf319f45ac908dccc5e510b67be82635a488234d9b3fd016dd2f09667d3", + "0x236822c1c20ce528a5d9a985c2a2eaf8349a3ff1dc13cb7f9a8c7bb5bc39e9de", + "0xc438b7d70a69768bb81ab867d6772133a592f7cf770931eb51e893c106461143", + "0xa49a416419f910f762ea095d4e8eddaaa7b01b406e730d3bc1846d24b6952302", + "0x08c83875d8ad54befb643c8d8dc684358aa22c144b9448820c0a62e5ceb9e9a0", + "0xd81a30c959e06eeeab28bc3b95a1c8a3466f2552d51ec0bde69a1d1f107f5405", + "0x76803ab074a7170033dbcc33a68d2b497998dfde8b9ff86101fd54539d1bd393", + "0xf7b9503ce1753623091cd263ff23af68851445bdf97c166358b890ff9dc78f17", + "0xbef1f09e8233542c73ea380abf0115279469e2bbe3abd557b89d12b514bc7a7f", + "0x8ef2348cff8d0d40e6f1b0973636915adb3ca63fec663383b8afac1a46dd0e8b", + "0x9724f0ce677572033e30756e4fd356680bc64d171acf7483d5dfc936267d3d8d", + "0xe5fe7147af6d1d1d52d34714de41a7cf7bedf38bff1a29a25263beab0f59fe61", + "0x3782ed2e5a049f3d877c8480b590a5d2055918c17804fad7ee7a6c2a4358bdc6", + "0x9bb40bd9cbb5d20c8987aa4e75b0a8036d0d10d87c46abee889d5fc23d10c98a", + "0x75d9d23fb96bde45550b2cb8ae4759ad36fbcbed51f00cf1bd1d04d0f5d0400f", + "0x5a5f79793464475b35bd7fc427a745b76541ab65d62e500d93e5357e588948ac", + "0x903e3a38194c6eb3cc98c857b98ef68bb1fc988220b12138ab40141335b2b9af", + "0x9c5c6d1fc8f1242a0f3f41e6dd9cb7c1ce6dc644b1ce552b3dc0ff7db83251f6", + "0x77b89a725a743a7b31b01022e4ffd1f35f733461828f204cc75f4f00290a01ba", + "0xabc38d580c92f199cc0aece60899fb6967ca48a096b98991dd757ed1fba6c36f", + "0x61742ed64d417b66d67e4638d0bb60d95f88cca17cb7630b1254ac3c47739df5", + "0x47f7bdedee6e8eec229eb0f66fad208a8a05450c8b649e2dc400e1b5759f4b1e", + "0xdeea16c07180d8a4eb104e309343225a354a3adfbf1b8af733456a185f3ca669", + "0x6df5b47f5b11511f42b4e981d92d55cd9aa3373fd90735cfc4c594c516159027", + "0x5a5af9e8656afd3b52f4e27002f1be9b09c5e7a3746fac7e64f5973ed423c14f", + "0x1ccb86d370ee32261f76a3e9d5948c40489c26f2f4216535484d7bd586f2851b", + "0x313dda0f2ce1542eb1236a1d555959516abe06951460d77664650b6cb7613d5f", + "0xfcf5408ed6675b5d1c15629e02635d726777e63817c3ccac36d4726bf95fc4db", + "0xbf5f15c67971a4b624a8ee8f81086fe24f1940a084bbfa031d0b69b865d553c6", + "0x9fbc4b0ebc2a128d391b68be84f6ef7833d5413bd95f751b503799a54c55c3c5", + "0x7ba3b1dec981980a957680b3c125328bf3683578828412c3de197ad07d06565c", + "0x4d2ea255cff42df73c96d18fc2c372e5ea9fe80eab5c98a6c6579725bf4c6fa1", + "0x9793db50b81ac75df1846e8b8898dccfa822d69bc4f99f6bcdc95ace2e3317fe", + "0x21c1959416e88792275d6eba88e8fa503c40a1f4a4b78cbab5b94e6c1ef714ea", + "0x3161a8a5b42bcf792b44bc3f7fef3606978b6bf716effb5ca1b0fa6b85518ae1", + "0x59ca1b7fd1f09941c911a4a6d0365e7a852a420a43af73ee0bd76f6413c6f3f6", + "0x86db4d45c982f195667fefc3de1af4a17b083b6f3700e47568ea43829e686e06", + "0xe5a713d52d696cbe65220fb30a4183ff9121ae1868967eee002cc032fa41b07b", + "0x446668d8777d9666222a04bc9963f89873387535f485490adbbd15094e6cb9db", + "0xda139e22348743ae9d600e9e2f651a717b8864f7bc8a4881a875945daa2645fe", + "0xae22c6f51fa8c11daf898addd39665d7bdf6d266d42ddabd790accd6538a6778", + "0x1059bd6151a5a3de93e45f7b3c498225b759a46444717cef5e66879b6e0a43ab", + "0x893e490bd7a3cef98d878f4f55d22aa0456f934e16a5ed635bab5d993909b006", + "0xefff7bff0a97e0e51718eff0d61540e164cb131ebaf466f01516ebe956b6e0f5", + "0xe6eef674ead50579daf941c526fdb904419a37f009fd432ce0521835b7ea09d1", + "0x7cb9c0e3aed016176058c80a03eea8f70e6f0467cc744ee752d937422ddf455e", + "0x995c33746119fe51b3a3c71311fbd4d91810a2ad95a558101bd11ff1a9913da5", + "0x926048c8fa6053aaf47f8986f195d19f1a86a4e01d5b34c45c91a09f57be0cfc", + "0x18ff8ac452c3f84463c8d8d92fa6f5550149f0d9cc0bf9b9a6ff6f05a8e3dac4", + "0xb495eef7e11375f1fdd6fa2fe3f97e1b834c39abe60ef1eb75333f36474a57c8", + "0x8dd386ddc871b764849a057f98a838f0f66883cdaea5ce4a9728910d3e1d124b", + "0x344c77e81e60bb40be5a20fa14b7fdc8c4c696d523cfcabc408e04df7fb79259", + "0x6b55d08a7eeb8f800866f031cf194c0ac2292923fe0df0460fedddcc77ba59ae", + "0xa007e70a725287607f23c03a782cb36ebecd692b45c1273b62769ddd1d0caf1d", + "0xb196d7291e437b6a26d006634de1edd3096367faacaa2b64b6c52a5d2228fd43", + "0x7f4f82aaff92c6e348d8b52e3bffddc7d06780b3750630d40022d54e668ec761", + "0x942e657c2125a5f57351b6e5fc76cf6ff6690529034f65d1bfa18ec8827bb241", + "0xce436799308200b480aa234ee2a4ad407bed1142999a360ce8a3d2230bfd4285", + "0x9f3fe03371f56c4afa64fbf2379c7dda3752eb19077a4de4b412fb7b5a4721be", + "0x53f63ddffee8a93759f68730026c3a8c9026a1a61a17ecd8b98d29e3d24dd019", + "0x08c2112cf1abe207076a67b43f67402df0d9d99990cec96e10ca46f4799134c0", + "0x41c4a5929a07431a1a0918caa5294b9b0f706960afe8e75b0e4555e40e182d7e", + "0x188b2fbd1b54709384dbc8b62e21cb1cde0b5c743aa3c25d8ce5ba4557728a54", + "0x92cc75e85ad9a09407bd0c09d68436fc419eda5c29614e125ff99964e304c0e8", + "0xfad03371877721fb4477f73575cee54356114f0842222f6a343357a15d409568", + "0x6072b8482ccdfe974790727775e8c0c43228a724068a39e351a5c702e7086fe3", + "0xdea9c30a8f46fe7e34b191d90b14fe09aa61275a32063ccca2d138886419c60a", + "0x3c24861389a3d1a8ed5d2c71e052914d4dabca490be956b7ed63a2195d65bf60", + "0xa2dd585af11cc6d60b55ab7b5295db6f5fd49e34c58948ece2ba6a4417f53fed", + "0xd0cd3dfcdee7cf3b00e718b6086ffbad37e61b80df49c373371b6ea449e5dfe9", + "0x018a9cccbbee610c3d978afce046e1e729013fc7ae71a0d80b2c22054acc32bf", + "0x6cd7707d257de792f35bc5d5080ae5bcfc50188ec61b406743f0390e8f5e4ee0", + "0xf036437c80029ef8595f65cede2446255b436ab2b47cacebcbc513ee09bbc6dc", + "0x438c8373efeb9d010e459097f7eef12dfd469a5d447f5494742321fcad2f5fbd", + "0x97174ad7515662e1cde2300a93986316ce4b7ae00b7f35a0908cde607ef6070e", + "0x8527198f8104df2b8559ff578ef5e2a84a9f0ffd63d8faedf26a50b44a19932c", + "0x7e4b79ce0fa7dfb5b4cfb92a98a0f4de105ac1a931a34efe94c16e29d5b03fa0", + "0xc240072b872710ac52ef170ca53ab03b3f76f1e2c1cc40dd48ebec3d0636d6cf", + "0xf957c0f8495186575f4a1bd2fc19350c9fed57fd60a884999d943e5fdca35038", + "0x814f0dfbdf3cddeaa502feb0f13f879223d9f0a14d01d756c3b3e773b44ce581", + "0x999f07e2f640408c58cb9cc0d15b4ff6c90db7b85e98e6888f74beefce295c1d", + "0x71e4362451b4d259a942b2a0425a070a9f0053aebface053d9c02f9ca4a5ff3f", + "0x28a29356d506b43e9dc9fc05bf6c7533e6670f5ab1122f49ac572bc2c9b94b49", + "0xa6dc1aeb3352b65845baa0329491789262c71b444171e36a55f3e20ab5edc026", + "0x6a0d71aee995e2857459513c124ccf8f81212ef0f14ea8a99b24b395e763e2b5", + "0xebd35e5b9134079efbdb4759e0c2af7728b73caeabc2c2bfdc3f590d5eaabc68", + "0xf1a7367e7b712b141602d204924a0c8808c68b013006b344e30d207a3b8c179a", + "0xb142818bb0edf0b1cc120389d0bef91b1a7b3e1fe0058810bbf73d04cd573147", + "0x8aef2ed196ca7cea305211c656084a46438ccdb7d2549f24fe4bd3fabf11af57", + "0x934cdfed19bc7321061d3ad217879cd14def18b18f6d8d2baa625626214d39a0", + "0x16f1b202a884faa4dcccf5d408b3acb639a78aa1104d1bcae40182f71d94a1f6", + "0x5cdb53c18168fa65c252659ba594552ed95150a4f0f2b9391ea3fb56badfe814", + "0x304558b9ab585c1201af4e88848d81f5fbc380341bfe8df9e9ebd279a94a1c4b", + "0x7c13cf0890b8fea28d01b7446750a5f1429d343ba69525e3909f054495d30f41", + "0x38ff978ee475767c435e5cbf5c0ba9c10c48426a9839735b67e2d32028a1582e", + "0x7fc90ebe34bd2cea4744c1ca0f3968a304f7cbb133353cd0aa76b45d0e88a23b", + "0xb0da2f67974a7cd5a291665ee3d216d5f57df1b66e7a2f36dabbd97cc68bcd0b", + "0xc220942a0821ea338d4307365bd1be586dc8b3a89f2b0f60065c92375b1bfcf5", + "0xda1019e4bac45eae0717a35e7aad0c63e0063ba18a8e970cd631112a9baa3d3f", + "0x7efc5e52c0a590ceabce9eb4eab90c50a1bdf595b7a59b0ffb3e3091317377c0", + "0x2347c7c6cc6f741e484eac237688427292a65d01f037ab25419d3c17d538db6f", + "0x207e2f3f0e856faab65882a7d3cf09daa37f08406cb96e5bb4252c8d0fa7f27d", + "0xc425eaba8494a111289148c65db0c8d6a6dc27d16986b0b70ee4995723c878f1", + "0xf7778a3d1b7fd83b3d4926d2022cf823801a2749283c9ab4f86f87126b9c7e80", + "0x1316c7953bb3170efc234d13e9632e017ec4f58a30e4e062ead373b206461f1d", + "0x9b4cd6a0e47f2c64c0ba4d9a7565f74e956cf881bdc33b1176617bf9f25af5de", + "0xa972bf247d8428e97218ec0e9a36e3b7fad10ce9a3126bdeb2cac7e9b4e48e46", + "0xc6a4e22ff93e8c9483dfd8eb8f2c384c72fe69cc4f5644e8d68d419b92509c61", + "0x601a9a84099f3205c6aecd034b53f586382379dc39be605facbc2da5f793b990", + "0xb7ed45da8acd237dc9c265afbaa15300cc7cf72c740bbcc00eabaf4dd90cf9b1", + "0x37bcee935e0ef47df22a7f9833b3b3a3830efb1656059c450a7e6a144798552d", + "0x06e7ecbc0f4d4b16a24d4260d77d0161c5a627083589747c72f765281ca8d3c4", + "0xc45d35b6c6a81240767ee9d994c8e2f5823d8a4e0f51abb7187e234d6dd5076c", + "0xd3c726980fdf096fa314533ca3e882cb1c1a2a5eba4b9ccef7193e9ab7ce34b6", + "0xa6c23887788743ac8bdc123c7b21873889d538f7acc389c28d6ac9dd26da06ec", + "0xb8b8e38541b9143da71d68cb705ad5076b089b6174d99bab4b87428d8828ac9c", + "0xa70ec737a97e306a66b7ab8a7866d03d496bdac04a08a0ccede2b84583ee5d21", + "0x6d43013f53b03cbaa4c82a2d42f5336306301bc7a624542387cb0a822ce89a50", + "0xc24661eb76bee7b5db00df129db5c2038dff544a90de77809a447530e8200d02", + "0xfd291a2a58d472bb262b3e9b4b2c08f8ca40536082e05c081ccd502c77a78fde", + "0xa34be368c12bd1c05ada73b647d72da9595b0d618679905c2df760ff1a2b6dfa", + "0x4e68946f1b2d9f9a1c10f90b0d354084561bf5440a823945c2d95f76fe23a765", + "0x44e04796d2516db3ec1d1b47b47776999614e81f6d300e2dacbd4d15746982ea", + "0xaac3047a84d57dac9b3f3c8ba907bec354f99ec2d35b915f115f4af6a658cb41", + "0x58489c0ccf49e147ec986ffeca33e037dedabc57690bdefeee7af8a70b781fbf", + "0x3707fd39b11fa9b79749a84848deba1c70534a493769788de1610798b30eb70a", + "0x891483f79855a712f99bf6bbad19a3291c4d417a4f27d66868951c454ad1bbbf", + "0xbac1fbd157279efe1b895138c55e8da4be5dac3b78c179c4cf25a0c8b5e7c5a2", + "0xed050c00def0f6b5936324a13f426cda7229ffc68c7f0eb0ff6dadda957c737f", + "0xb0e759e352ea7ccca080e4ed51a6a4aa4573c7c0d703183da7a98918fa9bed81", + "0x914fb73a4233f0368088073688994cbb0bf5f973a5afadc11076cd64f92ec5d3", + "0x153136092380ba6be166df58738c72d5be76acafcab31474a72b7f7e70e9296b", + "0x7d4d6f2fef46e1b87a73acc8c6eac29195286af79ed7e07998aa966c536f8f80", + "0xa0d10b259aa47815403230b836be272c3dde8cea738868ded0d55d89f2692dae", + "0x13ba7e74aeb0ad66c77cd5112e1ae1b6d3eea84a7c306d161408ef592f2f3399", + "0x491fe1f14cc862fa156f04901a7a93ef26ad272cf98e4f29d28a6250f8b4738a", + "0x92a8a01853ee89642fcf51e42e6a39616f8374937a98ea579d9fe35557d37bdc", + "0xc140bc3ebebc1509940644318dab91a6b8722a54bc32ac7500d94d1aa24ae1ed", + "0x55a5a8541d36a244b1d73b32fbe99263cd6a7b3d9b88312747de6d66302e2a63", + "0x510caebe6714728c47beb114e95177c3a0fc2438369842278a17da9c4327caa5", + "0x8ec8d3e19017cdd4bb4b789ad29bca53d8635596e6771e09becb9e261df64be9", + "0x2e832a7e47a73b47ae881794122f86e1a06e1d84c5bd0a946bb61175dbb5c0bc", + "0xbdb48c7ec1f278b646faca580983ddf4088835803fc0dc2e505be6b092442d04", + "0xcee8eee843b37076f11f69b77094872966f88b4629240e04cea69d4eb16d642c", + "0xede0d0302e40a6e71dd63a52fc1bea3828c4dd56c754b576af9c441ffe2945aa", + "0x07c84f827310d73cc44181f612f5f5ee0ac1207b3f72ae857f9f1fc0180e8454", + "0x40c4dddc5fff7a7a5372de4d64945900e1d6908f378d87b4bb75e425d1fd3daf", + "0x61d9dec3abd29d998af2cadfdc7d22344b651efc717797a486712dd10a726c01", + "0x9de133af95e03c3bacdc17ee428c0ee05b70ef2b1b515edd6fe488db3d091e88", + "0x109aa9704907a39615ccbd296974e428bf5fbac0bce5af02d9c3a8e45e47e160", + "0x8a1f2230da7f048dda7d17e7347cb1cdd2bbcc2ed0908980f22ec070d158689f", + "0x9e8215bd095daa6870eca13dd025edfc700bb5f5ae4f0ef9d44a85b04f15596d", + "0xf93f90b45c5e3358a73392f21fe640c6aff9cbf352eb7a1725088313630d6226", + "0x4e4c1ceb1177bd15944b238624f6e41aa0713564eccbd7f0976f22cfb03c90b9", + "0x2b7d1e8792dc0271b3802e79e4531e42d93475471d880de288dd4cfdd85155df", + "0xbffb3529844e19c7e24a3bad1fd0b90d8319c6982badd3716ec897ae01b0ff57", + "0x497725904d682be0024f5327c62907f1507530231fe6437341e85373aa941d03", + "0x20c7929ae689311a8edc8672b1ff85d79b2187c46da7d0f1e0decdda16eec6b9", + "0x87fec4a78f73a200c7798a07e9f7d956aafc83ab4a90309591bb76152b8e11e3", + "0xabe053b8ae209ab83d827b63e43e08b9b4228b04d51026bcbf044531a2e8f86f", + "0x4220e78d59f01c20f45273e5183a2a36d6d89c2f406df441e76fa8b3f540c8e4", + "0xbbf658cff95c4f1cff08b95069d5eecd60ce5960818014edd66b4f0761708827", + "0x885d316eed1f99685d4dfb1d50701b7a0c9892131a99bf1c676d53c15ba1f7d7", + "0x0fd86d1ee76af5eacc36255f29606e4574b93b1501762118173cfb9997996c78", + "0x400114dc52b491898bd47923cd5d91384ad6a728f725ffe87c916ba858e58a95", + "0xd489355bf13693c3c70b031421c885aa09772cb9d10381d6a5dad584f2055790", + "0xbbe033546ee5909557cb5b96d466ca32973b455f34bba60ddcdee782d8b6cadb", + "0x207bdb63fcf0b403bdbeef4f714da0393b1760adcd1f1f5b21a178bc404c9617", + "0xa30b9d2cffd283cba9de68eed716761ea78170a8d7e6444068d64563c141dd2f", + "0x995015315151329c91ad8ad28aac545c50196dabf0ae393c72d9f369524c9fc8", + "0x3928c0b178546ff50c5668677d2605b701b06f4d28af0c189e746b73b9d5c53d", + "0x46bc7b35d38eb9efecb3082436f28e3d0e36465e41222dd8b8b47fd9857de499", + "0x93d83ff35c0e00277205f12b3ccc89ae35c28177023f2c37b562df491dfd0e1a", + "0x11442d3d7778f2e463da87bfe957768e36087cd2b777e8b0d78fedc624b9a9e7", + "0x2c4ca247e8c280db854ac3ff7d270ca3100b30377b6ff807b853da46c7321113", + "0x0f72e138a21dd3ddd2913dd1eeae525cd78aa578b909672b4450158bcae26aa4", + "0x6a02b4d5e27880290806d410461c10558f33ea04ab655d291e45a7baae5d48d6", + "0x5698a8526618fe20a06bbaa970590e97ca9677ad95b0b532cc816b2577ec1bc7", + "0x7fb913cf94da8bfd05526a954c1f2e1382ae275a060b646d2ba78f004d67d30b", + "0x1aab7d83cd25cceeadc8352a9de9600b0b69acf33a3e157e5e54044a4b1baa60", + "0x03df372e7293b449518d07c4d672c76157c97c6bdc8d7ea75b5d37daca19dbb7", + "0xa928453adde66a9ea45a6b19560fef2619fde93a0a0ee463d64ce538fed0f8a2", + "0xf9cb142d5728bc1dfd59812afcaff8877e6357a46ac9da8beb54c1cdb53bf4f2", + "0x3c67d2318e1867ee0bf234129e7f9ff7aced4d6a94bc2493df3d49d3ef7a1f1b", + "0xe7995735d058d5f1281298652cfec16318f36d90aabb73bec620db045a4127db", + "0x796b4fc8ff8d962425ad653c20cd5e3cf5802edf8fc363824cbb4a117e5dd9c6", + "0x3e1f5f3519dcd5649bed8a4fce463bce7b8a28ce4853b2a34c219c76d8e64238", + "0x0b56a06fcc04ecab448189e170470bcee6415c859a59a2d18b5e9a838bcf2ff3", + "0xa8ceb396b19d4c72d6379f27d0a8283a06c7920811c045dd2521b20d719c9f60", + "0x42c02012a9f184d3bf976ecdb7f65e7abd70b4a32493d75cbf0c3403f5ca358e", + "0x79aa61d2303214ca5099ce8d8c8b59d7d3f64d4af9a009683ca4fe2ecd1ba4a2", + "0xbf626f7b1e4be0c3fbd962e021d50f40e580f133b6127a71ee06c621fe6508f1", + "0x3a8e93479758ef89aca8a3311a16fb82fef78b19ed11c058625e6bc6d4a4dab0", + "0xb44da67d7c6114b0578183dd1287bf06f0f8185a41c77ea75728e48954b4959b", + "0xf49609ba698b8085cccdd8f95a51996d9df32cfb8d0293c01182482dd03b56f2", + "0x94226b7a92c9aea67f94df39675af2f27cdc9a95c847eeedf85ffa17238b3b58", + "0xcb27475102016a2ac05f8c8718e110b74fffcd6c2a97ad0b344ff07a6a806e2b", + "0x84b0c9983378884ad2ecd01c7851efa8c0906b4741d42581c3f1c52c47a88151", + "0xa9071e8c2f56b564873a782630fbe3bd292337b2b620eaead27a397ee3b3aafd", + "0x8c057d25493e712878bd8b628b3cff7bb1249ae41dd34c25ff57a3bd12b9a96d", + "0x7c2b71dbda144cfea3c1e492f0a1e30f2699f3f1968da76a97fe39f3867d37bf", + "0x63b9d33e4d168389637e6fff44d525b67b5e0c52bfab9fbeb53df0ecdd601984", + "0x2a74b62ae8feff55542905a34d6f45c1441891130351bc9959c62f53864385b5", + "0x725a488e62ba0c296ee78267ce714a8ac84ecc3fb8dc4b5147024ee500821dd7", + "0x6c54d3f508c1421a60a707000e1236dd6b45becf483b1f21a53723188c1629b6", + "0xea9072804289a3b9fabd8652a56c2da6e661f14ba764f72949f7fce38e517e7d", + "0x65118fa4f148eb7b0feabd60c0ab9d42802a5ad7685b70bee9ef8eab668c046b", + "0xb07416ee467668d8c7f92a260a6fc430bf636e29b9d5ded3dfac9751e006eb4a", + "0x55385976c32b170335a70a213780cada5fb0c6dd5402cd3056a60a40a6f13afd", + "0x7d9f44575cfa89ae8f4e160a1f432e3e05d02c40b9d25ca0c06df6d35226985c", + "0x12774d382f48d5be794157e2a601c3814135f8cb961d534e685368c3d605f430", + "0x2be48bbedbb5d66af410fa612328524aba00374c152a0b533fb2f15e98bcce10", + "0xcceb249b515238c46a805cbd452395633f28864f16959cef39ca13b69acdcc2c", + "0x90b5204c90b256708cc946daad4a70e3cbbcae36356b377ea6cb251568d8e87b", + "0x468a2c9bd83eabbd10a5e301201e54b9530b9a376452096bbed509240c6d7b08", + "0x322456bcf4495d9787fb66d0d8f21242210167a1dc622706c647e1495c19d064", + "0x1708f1ba83981d7d6d21b8a7d55a4bd9c6355195bf42eb96e870a36480f90cd1", + "0x904c7c58505bb73503c5baefa79936d96df91a15a6ce8121c7f38c632dc7172a", + "0xb2592d5491583e30e4b0e89820f6cd518d4b082436ccbb3a7685c365fb6e9aa4", + "0xf2b08ac926804a68d4c1dce8fda9cc1eaf14e963499f29527caf4797fc47933f", + "0xf9274e64683f5717fc4bce6bb2590921be5a67c1c3f9275bc390a438bdee8295", + "0x7d6c4c1ddfd161753d2782c80532479eac3ae593ca4decbe0b4308123759a218", + "0x3215152f21280b1baf712191a11b3bb80d48f936221a02d199f52437d878c402", + "0x748ac2bba96f92574ff048d2e3100b8e8543c181c8e817f3770e405f24b4e3be", + "0x056d9dd029b79d40d3599b6f6bf240603931a247979db979dd9e6dacdcc53d94", + "0x56c57e769cb7f53c4ca489db4de4eec62071cbed1bd33759c27662e411de0221", + "0xfc7a497be0b417bc1557eb04ec43f9ba4432cb029b6684c295b84e500527d5da", + "0xb505ce1021402ccac337fd5c5346fa162c6843b2d945b44ed45afee0139df1c3", + "0x267595787775e5a0d97e1cdf0a4deaaa785c03b8456a6e5b433767bbadb00ab8", + "0xdfd8e51ed2325e8f2d2bbcefcd24bf9242fbfd75dac246ac8186cf3a72b35de9", + "0x11004185cc8175d91364e878229f5aa5a5366130152e1dd8132cd22cb0333086", + "0xf5ec1ef381b33decb885d21749cc32ad02cfc66392281967758c3fb30158f9b2", + "0xef7fce98b3054d4fc282b38aeb4bddab1c35b085ad4ce803856157d7fc9025ce", + "0x7acbcfac776f80cf46614121866a2621df9302b7be20c079e224cc1377637586", + "0xef8433ec6eca61afc8bf7e8ccef7e1284aaacbb4972938fe061086307a6417a4", + "0x1f76cba9e66ca16339c3effb745c71443bcae09a22f1b07e39155a1805402890", + "0xd720f64f8ae3da220074c029256cb459b1ee8be67ed4dcaec10933cad35fc58b", + "0xf841bac34a5db54625473826eab74d3a53f7b3448e5835cdf4df370624d24aa7", + "0x7b784f45b8f2b0ec9229924fcbe26f66c306297cacec42af1c474566e1f83f6f", + "0xeb333d1de57ffe52d60603c66bbe9f2941fdb01f81832d418307596c156cf54a", + "0x06abb43d2b3733811dcf3059f48be59587425b5d24c086689191e5bc69fa03a3", + "0x76ffcb35a00362f10fb36d68eb7676093c3e36a3fe7ad65acd0f58a064865f2d", + "0xbfa99b31b08807772e417d9ed67daebed645ccfa0d94607dc0cce754bfdce0a5", + "0xa91db056c961f435693a047611028c7fb784ca015f450e146bc8e2b85ac8d592", + "0xf2489993da543373e00b5b42b8db157eae15026a65635e5784d06702d6574de4", + "0x1629e6d410d12987f412c01d14a21ae700e4939f45ec4ef0e1820905649b7899", + "0xf127b506db1c5b3a1aa6bcbb2c530e822bea1ab3deb497c54430fec6a60824f5", + "0x4860301daa24a54ae1902388dd072e7eae2ad449f4a86c123caf783284cbb7a4", + "0xa54be84ce7432042663e40822685cd4c3c1a33bdddb295dc32f9170ba7c95a14", + "0x576136e6b6f941a6861e863989da38d7234f524886f278f8a57e88bf925088a4", + "0x08e84b8d1e15a7aa660666a346a0b90db8cbbee7cc086378d4020d86b1385511", + "0x9229a29e091b9184dda6d66e244af33b0c536eb7e61b7777a5bb0d02f0a283dc", + "0x58e34e5a073bacffd29a05f85320aa52625a9cd27675954f739f3f9229e3a163", + "0x25d2f9f20d7cedf8582931d6dd9ef68233f3af9caf6c9c6d5a10f2b9372358ab", + "0xad43fec2e1ea1ff81772fadf5e38a4f280305cb225567becda2cd518ce2f1dd9", + "0x05195e944c209b1eefbe1925827e976c19d065d18b3e23d883ae2086471c73ac", + "0x62232485944c27ed398d4928cd9ad1680765a4efa652a925b97ae26220229fed", + "0x735c916d78f40581506a4cf286e044a6d61afe6d70daf97d80f8c584ce41d940", + "0x8b50f9bc93ce7c7582ebbd77bddcbc3a123e90d43434d63aa4b5c397a18bd1e1", + "0xa270240950e22fd66879927f8c51322f0feaffaf09e7bba8829ce5dd841b9e56", + "0x6791446d6f864992e3b50fb590719af98aadd7f8af54e4c71ec474cf0db56cb0", + "0xa22feb28061c72ce02f7d6bf27c7d04049900ec24394a73ea678b90c100de62e", + "0x0fbb9f4ef4d048767b036f40babd8769e185c549cd71e2e46cde94b2fc1916e6", + "0xb32bf314ae6b3994bf2caaa716691eb2116289a796f968ace5c49482965d2037", + "0xf68f7edc391befc772458ce2dc9e5a1c3eb9d3c709c4e177535a14a863eb686f", + "0xee3c3122cf1f7890fcbf409897c6ca4a634b6a90df2d33cafd8dbe1fd2895ea5", + "0x44c407d967d458d6cdbec3e4fd90e0f5fce309f702f08df5f7fa7d4d66ab8d90", + "0x1e4c7644e30228dd70194e600a57f98c3501a598835ba35a3b0c99f53101b108", + "0xb619a4deb408b73810fd4c5a827c0cdc3f7f9885470f63c468794c7a0796c184", + "0x69ad24f8814b17603ff6ffc1f585346e243a296f74bc3781453a512bfb746a9b", + "0xd311dacba946134de52ed8f98e5a643c0b4f8d5c6d2c928ebc5087b33b309422", + "0x59ac40ae911e0a13aafdb4961c459a9cc8168e444f9288cec782911aea1e5548", + "0xfbf447c35dabf67ad4e88a41e30c40133f166adbebaa154f0c571028f86bb183", + "0x64792ffea472b80e6241942b1191a4e4228e79c4e0a436873ed3eb714338aff7", + "0x618ea5b82192c2e43742b8d74e78234efbcd6332d74c18c97ca087be66baa8e9", + "0x8e659da466b2150870fd5ccd6b4c7e879f3d5e248bb386869bcef965fef02837", + "0x4ef68ce8190a3430e4aa1497ccfc26e31d17ce522f1f16bb7280fa9ca2d377b4", + "0x79a10cac12b7e9b922d9c8d1cac99ff5616826a5f176a81536402cb968377e9e", + "0xa4fe7b019557ab1cd8d212da8704a0a84e8078eb9ab53952c2ebc0c3914835b7", + "0x59916f12d4cdc91b9a0e135d84630d10c48a803dd9b84ae35fe00c29d1acaf58", + "0x802690bc2c2f03e08e2d2dc7216a033be7eadcce3180f220fa5baf411cfce958", + "0x40fce55d2c349a7f97a252f6ae7d10013c396710905abdac17ca794ee1e247e1", + "0x73dc6101cee72bcc15f9ef398f90f2c5e3f616c97b0c6714b65e9a6a83edc190", + "0x7a179bc76526a5752b82b1556692acb502a72e364ba1295aee91abc726c23de6", + "0x313f3a435058fe29e7e9c490553bd9791dad94ceea2ba672a2a2a3e7f69039c8", + "0xb528f654e6456ae46ca764295454d78f4d5a4334d49340a754b5d8ac24b0d21f", + "0xc06aefcbe61ee635fefaf4e2e933c22c73c48b55f8672cc6a3c8ee09836af286", + "0x6626a0134d8ead3f18aa09af7b1c05c01be0cd88c286535115edf3ff19e8f06c", + "0x90051f7019b8d99acbeb816ee256899d8f0a5504f156475cacc631f427273c40", + "0xaa93485a22e1fc2da4c5b5483ad81208bbac2595e758e23677e93521106c1665", + "0xb334853c4ed01341742733a9b1d7ba7430f3353fc1a52d0751808c98f621ab51", + "0x67d2672662726fb24d884ea92668fea97d9e0d908a11f70bd29126fdcaed009d", + "0x09cc08cd5c0da5caaf74d0e8e417fe151129ee7ee9b5082f415f2ede57351dca", + "0xf87ef937db4618c977d871e06bcd6e2eabc7a20faa0ec0f08deff573200b0aef", + "0xaab334b96f82ec056e3c8e1e95b9326f35d57408ce24e63071e182dc38d552b9", + "0xa0d5f838bf15f771f9adf19f82e01308ec34111bda56e2edadf5806dbe80aa88", + "0xf288f085a08d5edafdd58a3a0637a48f0f6cbc730b0d3a0530a6780cea152281", + "0x8a9e915a3e1ac769bc5e3b308fb05cd49d6cc40e97f6cf8cf789f1cbebf7bd6a", + "0xfabd2bf75fef724ead94c7b6e24026264ec2728bf792bd0595aad04900c69640", + "0x3b4a6d3a6e62b3fdc14173ff99419ef02b18134050f8433080e65322bb0934a2", + "0x8d72cbb4e088da0a6725163af3aa47a69b63120f3bd02b93e12788b144e1a26f", + "0x0a112bde7c1434c444519ab9371a553ac25250067c72171fa6779efee4bdb7d6", + "0x8b89d89dfa4a2cd35757629c84edf71815311ade9742e48b6f28d1886f1741b5", + "0x538bd079c14a70a3f6db4b45b528e405a210fd6e2343fbdccec4235a09f016d3", + "0x95ea30334671da97054445f307f3b991ccf1fc965ef148000253bca44f6f4209", + "0xce98031baee0108eb9695be729938f22ee71abd8f368c7284fa7c102c2c03672", + "0x916fa5e6d29e5b17d29e6393f96f2707b2e245ef49a671ca30987c9d31223ef8", + "0x1caceadd646069b69b86699e555e0695c756108dce2c74ebedaa00b8f4c1d4cd", + "0x46a5419c22b3bca18ad94d7b0f34af90c62bb3cf660a4b0b6d28bdc11b079390", + "0xa83044af490b0706b350357979a9e648e041ea747ffec7a50f952b71b9c42a66", + "0x677dff9b0b4ca0f39d526ef4a8fac9bb49d83b5618f73d7ef78c3ace311fd071", + "0xc85d8672b034eaca3715eb6db5408d00d8157c991fbfb55b8fe29ce168aaec9d", + "0x89869cff97616604b696706b28474ae54d88387c2d5a30e690c0e5b31bffffd2", + "0x168585a4f4a9a745a588d609d3eced8e550f1d69a7399547a60456df218bf4d5", + "0xcb4d9270dd367735a6844dcc314782b708ea9b2de82e5f1fc11bc83b0970938a", + "0xb3b2ba42395b4c15d6de64f7ce010486ce1dae4e6b14f01901cf1f4b6a538d30", + "0x11601920286560aeb98fe1436a1f66e2c9ee69ed2ce11ef1eaa6b4b3be447787", + "0x45cdc9aea631ac46062e1d848c3a737da5f7f1d396177196e52b4ae1019a73b7", + "0x113956d33be8473ea7b52f062945a1fafe98a424d50b8b418eb1b4f8403f7036", + "0x2d5a740fe5eabb6fd783b9c47ee75166e2ef7916e75bd5fbc40fa0a6680d97e1", + "0x3bd16b4f04c059d78b8a28627039d6e47954d6a87543c611d6fc8e2e941a04de", + "0x86b1f14be4ac193baaac8092ca3d377f47dfc4e41b6b81e02dd51655fb590c67", + "0x1cc63d91541476745e3732b4220403bc3c994be2e62c6fb2e788c5a47af7445b", + "0x5d09f5a6d1570b09f03b79e869b7f55b344a2c5087bd4c2c06c8d4a85f49095e", + "0x6b48201724527b22e31898ab54616f0008afdf463b9afb6f0c516e7347bf0519", + "0xe79894ec014f7835965959a9914ec4620c6dedaeeabe7aaa791965b30d503908", + "0xe549b3679ff2dd8cca080bbf27826ebdcc14a6ef6ff6f06a2f2ec93a4fb1baf6", + "0x5bbb4f98a26c9c9fa8ecb2b71bbe032c0c4d349bfeaa81790eeb066412de248f", + "0x614dbf21c81332c690c934bd9d9f4c640f3df9c9510e9aecd105f5d2563a6071", + "0x44231b331eabb55fc08363ce5da10d7df42626b94fc9dc4d08f99ac220ee145c", + "0x3e22a80bfbb6e99438d4394700bd26964ca97de1214b20c8a69fd1788c50a4c0", + "0x280ee234189ec732bec0b7a25b9bff60b04e7be32c54fcb95121b14725bf62a4", + "0xd94627e7ecf1abc160c432aed8c677eeb4a0502b3100737567bb2d19b1c81478", + "0x6b2de1031473d511a197f131e579522f8bb2bf9abfe7ee561d8e7ef9b1912b6b", + "0x80b1447503f606ec08175cdf5cadf774f3d0d2a1f447ba41ee66941811690b68", + "0xd0b95ce90aa06affd35cfe7d1f3b45fc121bdf2565ee5d3a1998a932c2fb6db9", + "0x4575abb623e43a73f1be5ee52988e2820cf35170b68c6ba8caeaff086783a1eb", + "0xa03708f13596c11a3b76c49b44102350c06aee7ba92b3f2bf3829952f6fd6c86", + "0x4916a6e3dbbcbc8a13c91f4db3f0695f94cca48b81cfd56b9836e7ea44af8daf", + "0xb73b5c9471a175c8eba25a4df896302b65af142819e047928b8847a5261e13ce", + "0xc5e4bf50a5d4825fcc44eadc3877bbbae0589fb5a2e9ce1b16bf9fd60056af5c", + "0x43abc2f7f189448316cec758251cc7b63526e95b16ad919e74fb50a9831b76fa", + "0x072a4368d93c89d6bf3beee22c6df5acac53b9c8225e53bf3e368739890c2bf3", + "0x395bc2072a773c314c9cfa3c9983f7d8d23fd77276a6cacc93f580405ffd03e3", + "0x50528bae45e8dff5a7d4533bd413b103edd5097c78f35f49d0674c6f4378f7d0", + "0x02c2b72bb7fcdc07939d7fcd641dc148ce0699e5b9c76e7af3984c36a7da08ad", + "0x0b6ce8ccf42bce819e02e2b0f725830fbf136095e361806a2b5eee5ec975af90", + "0x59da0fc5802651044d72946d162d95412e98e3a3bbc170139a5a54a9d0330a70", + "0x2d2f3bf4346e6f38045e925bcb90656bbac77f7edf2182a3c6835b2f6e61f9f8", + "0x213ddcd0341b130de4699b81b7eeaf93126907e52ee92213ecac458bd957eb5f", + "0x0ffbbec5a21fa439c09f975abc684ad9884d396433aaa4f4890e5e377d7cb4c6", + "0xf52462396c136854067622763c21ee796e880115bdf0fa08a07a130ced1031b4", + "0xfc9bd5b2de2b2ca5b16a7f7c608ed4218f3ab60499a691843548e530123ffdae", + "0x8eb92cab2abcc01f735011f27566324816450dfc5ae2d0af14137028b046bc29", + "0x0795e1456a6894c0ddfc3ff3cfa979855e39179b03c15285ce084bd483067a63", + "0xb8b428f8ff1ceea1e694133996663d000a9bf0304abceb7cdaeb6db5be74983d", + "0xe682c8aefc32e0ea3899f6708057ee708ce46125462e8dc2b2d4605f456e3539", + "0x100f58fdafde5a4e2070784f54088d7522f2537ab249095966cfc9b8250f6bc7", + "0x8efb5cf8633396437ab174527ced3ede99be7e20790ef382b5e25e49b7f49c23", + "0x196ee4cee920a9faa42cd5f3262af5b35dd212169550664a3c9c15bb3a3fd86a", + "0xd6781eebac997c5b8eed3b0058a058b3f6da487d143fee14711398a017d16333", + "0x096aaef445abfcd0de2a662b16cba39ce371d053fae63c27f3599df3d169ed0a", + "0x3eee47ffe2e9d5b29c5e1139911d57d3a3d8d87b4ef290dd95e88a8d9a56d890", + "0xacb1b75fdbc4c4850565ab27978e7d10f7c29d6528e7bf198958238a4be4846c", + "0xd0cd5035132f5f8b303aaaf10d6873482b4870a74965d8055c48e8449935b76b", + "0xe65aa97a1cd3aa5b9cc906157f7d9352cf6592abbeb66647623632a9f3a93b07", + "0x76d08e7ef145ee5f393fad5faca53efb07b9fb493157a6e0b290525127b5cabd", + "0x533af4ec13c168f2e7c7368cba5ab0a138a5633656b8c19f284c98c42018d849", + "0x0d61ae94270aa651231f1933cb968bfe0cd2f4e097ae2f0ad8d41b8e2a785327", + "0xc22f8e6418938369b4e505425a28488c54571c4a803df35a233f70ebb933c635", + "0x743c0c8d16b790edf5e51a8453601ada99965007cb4dda51c82cd68e7d77a224", + "0xe5a62da310591a98f6b3138ee367ad2a334cd48d430399c425b71a57ba44a46b", + "0x5a7dad70619d620e5991b7476d125af8db51b67935a49c786b57f09d3fbc4035", + "0x1459d52e06d83b9ef94f035d08af0d63f66ce87bdfc3fa25984292aa2680ec68", + "0x2139af4823664ad7b8f2d6222f86a5feb7a5df0f1a934e8af139d73a6d008d84", + "0x31a7000f3b576d91c543c5ffc4dff45205aee1120d9732c3a342318bbc4d72a7", + "0xe046d4e9ea0c367a2b47a0a23181e144d95f9a3b8d77244fd70384f031fba4b1", + "0x294de2ab05861fb25db6e1392e8eca830217680610165a2dc3f6131eea1d9918", + "0x1f2dc7721a6ec2f13b9c9dcfe3d166b323c53dac5ed49c883fb6104caa841184", + "0x3b2797e3eb732182ff4928045800ce8837b7b4796d9af977b118c080ebc1099c", + "0x4f076d935900c3cdcce4ca42e206caa7e63dbc926dbe230f640807bbb7965c60", + "0xcf8dd9699ad2fb397f19b16b851d85a565b4d197735f8e3c279de61fc4e45bdc", + "0x3d3544db3dc8e8fe1b672ec5ae8c341524bc7e1eb3c96bec3867ae910d7b85a7", + "0xb7d7bfdba9ead1c68bcd54c46c7ac2a34f5292333f62e7b91b193baee6c09037", + "0xa8fc4eea921532a7fe31196f68a915f6f935b8ab480b9943dfddb7279cc069f6", + "0x6c749c6ae083721c5f5613a581435fb837166bcb3f07325873ba321de17f4526", + "0xed17ee542d4953c8bd4a7035bd34be4e6e80b73ed19f86d3198c7efb661ea78d", + "0x24e11ece5e106eb20534043c2c9452d5faf327e0555bc3b8f923c108781dc1d9", + "0x094a0636d62e402344d2bc9c01fa145ba0f32e03383e5eeaf85b257eeac3acc1", + "0x0e02c54a2ddff2892841d6e450d8deb610057e559ade4b8c3c943bca63b31384", + "0xcf147eda0c90b66e59c407ae4b2edb236270672ffc4bb68ac7da7a23b0274c84", + "0xae8054abfb1b72499642a11f80348857e49aa315b8debd3d61cad8662ef56b08", + "0x8f9429544ab9309d5a18a2af44ae6bcaf2e04de344d9d55cf22d8c503caab64b", + "0x8c6288b1027063bde39e399be8e7c82a7f8f9b853fbc04a32f73404945002e4e", + "0x189a06ad980a6e9710acb970572b8c04c2fc041fdd169ccde19b5a939b69cd81", + "0x0ae24fe54c7724a8c075eed2813b90e1c520119e2eaab0a0578e8759868e3ae4", + "0x5d22a1a2f551aa1bef7b552ac797f5b2a22441f7c3fbdbd017f4c5dea89b7842", + "0xc1827d89c778f3fb6c35842aaf940bf8a829079e008421db792cf04018bc2b03", + "0xcd9547011c12cef89331daa486d5a89bf36c8dab81d5d0e4abdd9c2e35d2bd8f", + "0xf115534a66431250450d68d8c1ef80ab9a3c68e63a203094ccee81fcce0213dc", + "0xbe9a16558434d1f940ac08ce7bb0ed0b1e400583b7bcbd06dbd9fa933d1de6c6", + "0x692dc4f061262a3f2a50765773294909d03477b0663f47f4aa0f675f2c0cf0d4", + "0x6127590ebcd139f736331080139c1b3bc6aed2f3c961732395ace62edec7ca5f", + "0xdc75a2ea1a80516dda32d61a2d671fcd421ab9ecd81473230ff9d3be8aab444c", + "0x369e3a3621551d67f48e544d76a815cfcd9a72b188972b02c12ec433e54c5f7d", + "0x195110af607c6bf30e268ae88dc0245c6d3a34d392f0d7698e51eceb1e10ef4e", + "0x950743ee4492e360e7a594dbc4b786ef4ca1b061210fe967bb5acad5622f43fe", + "0x926f538cfba0853b453169710a7acd6c2a002b87d2826374bb55790ddeff417e", + "0xb93a4ff2a88a8ef3ac4c77b55e828172f20b9b9ed655ba7a37b00ffb524b5138", + "0x08947d220add3e64d2db582c988faa58556b11257b9b4d8ee817122085145340", + "0x28ce559274d5455f0f9db83b263ceb6f1a2bfc00a2512931b04cfa23e7887612", + "0xdeea519fbf4ff441c8e61ec1f0ade9258a50a10a4702d0be90b408f30b27aed8", + "0x3aa93241858d4d882409dacdb7e716e6fc8bbd19587c15e349ddd5a7d90e287b", + "0x87a42a8eb92cd20a8dea17bb9a241354559443dbb1b4f549e2f9764321a1549d", + "0x365f92d4e3e88e73a155edf264ff3adad231b6a8eb9de11a1fdd473d9f2fd27f", + "0x85056e53a4b8368bf1ac1327db65d01d35e72da358274dc7c0d47a924615dc87", + "0x0fa77604f4d0a3bfbcbe9019207fd21dfdb118d94b391c8f3467b7ede0085494", + "0xc58d6bcd06d9a666bd5217cc75ec70c65d90f45d3266d78262d44dd1c48853bc", + "0xce225ea3d493c85c9ff2719af2d7316577c7c87e43598210446575fa10263038", + "0x010a27979371a2f4c5790ec5d61aa1ea443347d1e8622b4869fa33816306afda", + "0x34cffe4d5ce1449e6c1efc3c6ef2dfa24b3502370dd847b536a41976cc1e7e55", + "0x423da7e80d01cdb061b17cce2dca3f31fb62c25ce0b25c7ee5a45df3ca02e72b", + "0xff680f2c9fe4896d1e117bdf7e655d7d86baeb0753ed21b373a621d954be262b", + "0x9527f4e412a189e2e1bdccf0217cb6159b912c0c3765d5ea7f9e54e2cb393b48", + "0xa14f94bab1a626a844886e0338510b09edc8eaf9f3b416787b72bb2729d22c3c", + "0xde5afc9aba2c6db5ab784212bc940193db7f03b695049dc969f1d5ddadbd722d", + "0xdb39c68d462695c642bb704f1fd8de951d4752bd15e25115032a5b36fbc6d2c4", + "0x64987238a6b0d048f5747a81ed04806e89108ac247e5ffb3fba66fd875797690", + "0x0eabaca3229eed1341b2e330bdfa87432bbb685a116d02f943dda9ac66b4e877", + "0x2247273340e0c7954df9a5db6700911a4f786eea68c98106a29ba0c3abe96baf", + "0xaa035892630e9168378963dc34c427fe2146d6bffc6c8d1c5026ef8c3ccf395d", + "0x26a8e42afb55511f170429010e07fcefdce8bff03e3acbb3f8571be05960bb6a", + "0xa95bd3416ceb31df25f3b5e262109680341f742c11580e2e83da5f36d8b84ea2", + "0x09929d531b919f43ccf5b6ab7e87265ede17ee5b4103fb3fe2bf608fe9075c25", + "0xe6ec4893fa4840d90006defa51d5599b5a164f0e19c54a624430b5e7be1f6280", + "0xc21953eb5683f71adf5e5229286e6167c48dd68f2599d7cc35817b4b9ea0dc35", + "0xf30d268431af3d017f42363da727dc3f4fb42c6c1397a2417f8a6d862131610e", + "0x8a686f06e784dedc2b9e015d7ca5232deda4df832428050361c4c24639898c40", + "0xac5ced4388aa2f66869f6c3f72de0e531787bd415c8472e1a491cf750b4d2d41", + "0x5f4bd5ed18dbc2dbfa1662c45363fbb662337f8253cb1260dfa54ef496b51ce5", + "0x67b8eb13416e83606e2b1544a02e0172a1a9c1b9cba1d56613d2c1ac4c243966", + "0x3f450fd3a47b94ad683939f8af79603cc390bfe4747c6c013321e39f6f1807ed", + "0xf7f5ddfaf17bdbf632bab24d9aa0bb6a960e0e7bf1ea3efb3b60bd558aa357fa", + "0xe00cad28e524f273884ed368866865d466c0afed78390d8586c2660944e0d007", + "0x370dc3ee72f58da1a1cfc6d5311c688f171251c46b322d07f61548c41948ee36", + "0xd5b426cb386ed0f0017206e0c551cb2ef9f3cf02a3d94f68cb499f8f900f740b", + "0x837bbfb8d1db4e394c5917798b251d7281e4e1d024d95a0f8e8963ffa0a3fd95", + "0x2e7b8bd8121d24134418f88bbf49f8888357fedb1ffe1f928ab0ed4d3ef96a1a", + "0xa695fa51c83d2eadfc3b3775c200a8b39a239aeec091b281a022c9a3aed96b7a", + "0xff4ce70579c4ebed37684fc8f2d774cc358ca29a54d2520989cfb66444a4449d", + "0x7a34a4cf28f43b160218d77f742251b92f8ce3b627c23f632fed544e9234b2d9", + "0x67a36d6b2f04573dce9a2f0adc37bb9922ab038d2e706330ab6bf72b60afd88e", + "0x5c81c0bf60c07bd82794389b682be408a09b34dfbf016453a174229152e0b763", + "0xbc48cdfd853ef43a5982252eabd16b0ce575690195fa2cfe12a66fe10b3555dd", + "0x2125afff5e4a9e2451df3955e3bd17c8f6b10e628f8c3060a30ebf6bc4c616da", + "0xcf447b12f03d41da20fce5d8046559be590173f902e96ebf3c9b037398fae548", + "0x58b29b2b214e0c58bd8e71fdceca3ba0825bf7614a504e7bceb87c7f13ae41d0", + "0x1683843f47ac40ad0ebee6ba68a87894fc94e37a11433882a8d9363bf7bf4b44", + "0x17d702c3071014ef41b20d93cf42084dc31db7c97121f2f41e1b0a8ba2c7d557", + "0xc3f57d154b1047b3cfcf25b393aea31e0c87dc20cd601a62fae58bd56158e30f", + "0x1034d9216d4db99620ed2a256c454cdfd9e803e1c066eda49bd2ba1288d59d2c", + "0xb4f23a426c5458e52a667cb01c734d6d7473cf0900139da17d355cb679d0406a", + "0x16b7beff5ba9edc9442bda4b46e34afb8fc1ed917d226a4beeb6d9d2410d8fdf", + "0x3b2cf0a7782ef7e1a1b4590df0930c44ec2053ec2f519612b0f20f6e48f47107", + "0xbde784b24ded1a05e6d650b238021e904c72cb35d02dfb6fce107925ac947873", + "0x7a42213f3c331c3d183703dcf4e7704b696728c6de4296161b1fe73c78277a01", + "0xaaa0c71970eee0f71ee064dc6c908dfa1d2c0637983a18a78ad6284a72a5e010", + "0x72f2af281d44553d2cc0645c0b1a01a9a089c282344a4d2d2e5271594c58ec3b", + "0x46c6cddfaaf1d204e735c4804be955fbd0c3aa2720a6d40640641b4e777b18e1", + "0x7e52fbfa76e745f3cfd6e0e0620e7597afb4cac20374e8a54c56dec4fb1d040b", + "0x69ac36b338093a55343f7b1e3b2288965e7113f255d34add5d08211932c35b1d", + "0x9cb03dc2371657f840b5da9fd420c4202176168b1411fbce0eb93eec3c342229", + "0x14eaee01119803c9c427f990418126a03d1ee9b829b23d78b11ad94e29f92c1a", + "0xfee4f3621e875f9b69254865849a58538cc742e79843ba229df2a179bceb5b82", + "0xdfbc9322655f9f2ab5e918c2dcf819f0b4d6de07905dd2f94607e3b564275f57", + "0x07c42d1a39c3fa351ae4aa0adf903c39f3f7ba020776293b0ea5250b09c4cfcd", + "0xad6101974f08962920d9edfcfe8bfa4181d9465c1a571b128a5767406c7c890a", + "0x9f29341ba8b368f9c4dacb12094979a0e9bf2f6969c5262a880e974ce98c2aee", + "0xbcb3768fca1e380de80628bc55e911aac7a3e9810794ab9b86fd2ccbf3a727cf", + "0x18d8a90af389f112f1229b591f3898ea5a0f2a6ea46eab7aec3c99d82ea40760", + "0xfcb7860baa54275ba72b34069fd8874ef29a22331b65586671ca2ce88410cc14", + "0xe8323d318b6d03ddedf163b07961e747830f51a8b7ba90ef4b568305d5a8589d", + "0xd7d9d5853392e3cf51e6a4b9b8d60e5e1eb0e00962bf70270ae7f3a64e2efcab", + "0x2f6cae0f3f4facc6d5b1e87f4dfb2698211cbb6429c00af32af1b8963aab2750", + "0x4fe11c9667cecc0cb8ffa78a2ae76cda09adb8c993746149bc322dd4dbb389f3", + "0x1d7c6bd7731617983089a3a450e3cabb5c9e48873a516d08646c9b90ec152615", + "0xf3cb5662cd5414b486fe8d664a799550957422ce0b990f36bdbd9403ceb9afbd", + "0xe0da2757036de2ef8539c522ecaea947e47b351f68a1426c86ec621ed4bf54cd", + "0xa865a7275662e1f977b260ba85d5ecf1236fba2ac01a69aed047b2d261ed2610", + "0x780cfe59c907e3db653c573888464c41c4b8883875fceb57501f60ea34fb1322", + "0x754f80993771441ad0836d84a0233bfde1a4fc0fd20e199c1a0b6a755932b83f", + "0x9253cdd043387ddae4da68098b995268b3983a17a200c8990b4de3c3102042fb", + "0x4aa535ff3fcf8ca9ba6ceabee3d59fedfa3c7632cf68fbbd3743ea9c86686fb8", + "0x4f5110933431db2c0880d9d4699e33e8f4bacc31e4971801ad968cc25f064d44", + "0xe4443aff0965d78b747dbe5b7e92b03b4c1898e7fd9911177f3afc3f8e765fc9", + "0xc7113de3a6ed094a63f9d1cc5f68d29801ec550d8dd48fcf1296f10b73f97988", + "0xd707b5f7b09c7b264e323df29236aadbc03389a9fd75da07c73b9eb76ef928ec", + "0xc671f6c1003e8a2fa46a0f6100b1ebe5fb4657b8a3bcc8c4190de8a5770e2bd7", + "0xa1f8030d4bb2d37062ecd95a87dce8b3ce7bc7be55de7aa1028adcdab436cb96", + "0xef9a06d2dc5c209a7f7747128c22b17c2724d62b655f07b57fbde1ec5926a168", + "0x82b15ac8a4341283ca4177e0e7b89378367e205ff8fd59e3e43439dae8d6101e", + "0xcf641779c36577bf02101d739b7b1bc19f1788b22d72f473bb4129a44869c26f", + "0xee111a9312dde0a65002434b38bac07e127167463068d047fc95eb53469dfa5a", + "0xc21dce0be4bf464ff63c59b104f3a9e8ac9a5900cdd77b7fc6676e4e2a17f260", + "0x546772808ee95a5453a95e581b86ccada861e2f37840de932c70ddf65450779c", + "0x748dd0b051d0dc1e5cf18584c1111b1fd7648b62d0f340539eaaf2ca155e292b", + "0x668719274b07db2b66d0f2bd7e5efb6e18455f25e49b07bad9d6c046ae8543bf", + "0x305c577aaa37c8f67c631de5a2ff8833841daa6a1f52e8f860e7b6cfaba5e921", + "0xa7a197c39bcfb74d5f3c523de12af1651b76106f422c1f52fed35e6628749804", + "0x6b46e5b4a447ab9f64a529756a689db73706c626a94b5fee28415c33c0175e94", + "0xedd9664015849f4dba53c83f75e8f86e44f8b79d3fb9bd48e526cabc85b8501d", + "0x218bf5e9a2db20afdbeb167f75574adacbf64463f11665f2b8be2b76d741fb95", + "0x9f1c7175bb294b7e24a5eb61de4da073a5af547e7dc911c9b05d98ef64358d58", + "0xd6d859b88c49be0bbc83b1d34d7c87331bf281136146651d3320ad49db320f30", + "0x3b4ecdff0b3eb94c8d7acc0e101b7fbee9aff87992fb2431fe2c4d9f44a394af", + "0xd33de0cf69370a33f13d9d7db35e3490c88dec0ee23b71ed040338049616e274", + "0x84d004e9a8ba6b1bac42e8d6bc1253ff21b47a1fb494e5d19caa1bcf34b6c22f", + "0x71057ea68aacfbaa3655aa22c75a22fae502a9afce8aa206a220d96654b9d4e0", + "0x8a47b01548946acdd553570ad9b9bf3c78bda4f96fba851ee9b0a94b355d1c55", + "0x53b7a7ae02459b17760ad47f69dec212ed628d3fe856af16a0568468e7a0b752", + "0x66ee4b75bd15f446fbbb3f1a9f0f3a159b8f6c66f708917156d7fee5fe64e6b6", + "0x706b81fb39b3e09eba9e83250f2b3e2b567f9fb1c397125fc0157e346d5ea352", + "0xcca91f2ce2b51595cfb86e0a7458d45654f61d35a66e49d25e6dab01ebb350c6", + "0x4397d4d33ec5ad07b3a583e9f39df02f5756dbca92fbfdfed59a11cbcf9e08b5", + "0x88ad8498c15ebfbdec6b1f68deb7515746e5c110910150eabadaafcf715a50ad", + "0x715d175a3badf17771c686793223fbd837f2579ed674539eebb0ce1244fd45c6", + "0x96acfa1d0cc7475359b4f1ee596aa2975a87f314e80d4baa0a6af7c7c77bb92b", + "0x26b4c14547c255841bac2852456f386e8addf8cd8c45cc9f4be0377e05411bbc", + "0x124524955310b1a35fdd19104b6c0bfef503541fa55d0b7c24efdb8d0cb33a4c", + "0x9806202158a5a5421e94f69a904670c161bf61ab1c41bd5d104ad9ba63866067", + "0xf156a5c71bbe49c45f91ec3185654ef8cc446e9d18d2ae6d51ea0f0409fc89b1", + "0x7003d7fb1b01fb6e036fa33ca535bcfb836415e7661277634c47c628d81bc140", + "0x6f23e519c11871c12185c1af12b8840ede513e28877f7e860603703b3853b14b", + "0x3aafd215a348dc20c5ee5444cc007cfa0e90157bd33c8a4c4de4fb5b7a231ea6", + "0xe8f6cdda4ae600ab617fb40d96d0b2c13ead6ed5e36eaac9f8cff9df918b35f1", + "0x75e63daa0a330a4b52efe0e0e447b345622fe35586037ae88e6e48a8b544a087", + "0x6c4c8865b2673508e4c62dcc711e9886e57043d758a60787c6241578670550bf", + "0x21ed5b252ba5e9607045ec6ec85455db4615a1fc43e22138ab3a087c5e6236ba", + "0x2e2ccc6f66368ae083905358a182b0cf3e85330dd2a3875a846baba61c9be3ca", + "0x4a8987c10833a298ff22cc199be4816aa05f25e39a10bf007e72790081ccc26c", + "0x7a4b05fa47d37c86aabe456bda0ae7bfea3b28a375697d4d19ea2b1676425016", + "0xe203aec00f8bed45f3cb0911860093a5fe228c22476f189f2dfe4621145f49ba", + "0x7b2ba1b4da99ad50a33de156eb412d0020efc63c85d0840af6d9ff9199ff7dd0", + "0x0b84a8362a67c9cd715fbb09e0a48e046afafe8814ea7e1c0bc99da9ca670bce", + "0xb9457e83e503f6b65bd357b0c1cfb655b4c157e213c1aa2e4ebe6b88524c20fe", + "0x401fa17417f9c8f21156a3e660fa2b0a27eea8fb287bda37ca309a3c918d1c91", + "0x8bd1e0e83f35107a65777a1889afef35f2d89e70e9ef3fd56a0fe77b5a5a07f7", + "0xafba338c738de0a748fff79650392894482f094f79bf0c88b871324a6db0b1a2", + "0x40e66643c2f93c60de74a976f7f05d753ab09d33601cc1421da1e987378be84b", + "0x4a5d0c268a86af032703b688745b91bde7090b4e3b245f235056b5d675760685", + "0xa8d7233658eb85c4a309293e35aa9768768e78b9635ff989b4a5a1b64f6f26b1", + "0x7b30cf0f31a942afa35ee2b30bd3e67cf0fc3c366eb35b1e331ad8805e9e0d40", + "0x5730206b218be1da571ff8022b975412746f88606ee3014626a03eac8893deb0", + "0x4dc767b54a2e685d7588f6fcc1468cfa7c7e47474297b29b15957b2f21fb90d2", + "0x528933096ece3ea9bd4b3e0fa78ecc95915eac29b8b608af3b18eb7fd4adaf50", + "0xbbbe02a2b2949c73839afa56e959b23d3dff141776137e5e804d5d37a69ecba7", + "0xd8c4081e5c0ffe7797f07db6e5696e04ff097410b1e45a649bca43362366bd87", + "0x46402c1f08238694bc32bcc396093230cd2e522c54dbf7d00d042986d22f00b3", + "0xfcfff397df45c333b4fdf271b63a4510c45310a3c9cc78b97b867372203228d6", + "0x6d2350213ec1c6a79fb7dfcb5a66f065fbc41d4250075d81b7647e4db991543c", + "0x393995f9a4a358d13b5fd9684510068f9fb86c71f2382230999bb36b41586a56", + "0xb3b65d3c835ed1aeed86f34117431fc46d62ee2209db054a5e04cc81c1f795fc", + "0xd1ed46b6e64544acd6f9fffb126fea4b74de84664c2b22c760eb89878cd44ad7", + "0xf21c944b3e41a4f630ad5452a28633c885427b386ca81ec9814ca14bc94bb2ed", + "0x54c5c6c093bd6cf2cc72e058fbeb26012d71fc8e2bc6987fbdadaaa48ccffec4", + "0xa2e79a266c6044703233fea5d0c31bcc22ae247eeb55a982bc2e1c0c64af1468", + "0xafd38477bf1cbad227b489818e335a79d38bd052f7957efb2d07b9adaff6c68c", + "0x4872bf318a2066d5338e31929da36fca3e2390d1dc8dfe88eae5ef854b81bf97", + "0x896b1e0fb7a6daa96f00f7ef633ad67bc4462c962ec288d7f924c64c978f21f9", + "0x0c3617575c8a625e7fe35c30312b147be400b2777ac4b1c0fd84ee233175faf1", + "0x6f47126daaecfa7b84568c26b02ee779763c9f63b4b7f5cb0cf0ccd4a2c0a499", + "0xedf2e0e03c0d321911693c1554254d82c764f2987dcb5c76a4204b1184906abf", + "0x5af810bb6ef491ccccf2dd00c658bddd02a1124ca26be59c97a69199605a2d8c", + "0xf8a597f22125a6f3fd76e28db6ca04fde9f5eff62b78e162855b9a48c51a0cf9", + "0x9b50cf0abbc883c6af57fb52c48defb2ae67f77de2f2ef5aa4fd42f6236f3f43", + "0x6eca9c995386aa4f161e6f4c6ff999cf6bf1bb4184d9beab567abaccfce9fc1d", + "0x7d7dd300658f784ec68cc3021a54680ac5767085657bf87ab6f76f05e505e6eb", + "0x1eab27e441a43a3534507ba0b5390a13c7df373813131b09b2eaabab179b5cdb", + "0x45efe5848f21e2b4f670bf06f0067fe453eb29ef45df102588c7b8f902005066", + "0x086f17e6047e9476d828eab4ba0016e3a4a0f51f25eace2c27445b5fc28926be", + "0x63547a7274cbc857d547291923cc9e507e3f4b6fe85b1ea88a527674f15403ce", + "0xdb0d2c2f050c4235e9fd03de4c491e09061ddfb4e55fda8ea25a8c8f013193c4", + "0x5f5bf14f72834ddb7ef96affc52b3461d0512fd9bb0337313cb7cedbfd858228", + "0x92c4250a5ba9ec5890c39a608770d500ac31ae5da1cc26c05aca55a453e15a25", + "0x4fb2c385632f512303dd76f4c9a3888837227bbb5e5b949a7b9d74906ea6d015", + "0x4814397680a72cd024c3df1faf854d03c0d308b74c93fb846e620de95de7bcbf", + "0x068512253a6b5ccc302e494d1b17ab082f2ff4ee710c58d345d6de4a436fc3ed", + "0xbcba2fe1b54c6fbe1438fcc26e955ecce58c5f30f4f19219195a413fbf1c6eba", + "0x29ad9a11da3f646ae4ef3a5202abab2ed8a7058f336a6039a5c41d3619752d6d", + "0x5fc2a5edf9264de88df58c3bbc705b8e73bba19179d097f414598bd4b5fd63bb", + "0x9592c969a049ac723d756bd2ffd95cf599087a1da14ee9f472ca07e7e54f8ed4", + "0x735ace051a5e3896a9dcc788654f95cf0dd9cc98eea1b5d8d7340f9e51eba698", + "0xed5ec9f70d613be406c237ab7e8e2480c7a81cc666316d984bb3f9c67e10d6da", + "0x3b8248b4de2768d9a4ed2ce07c8735103b480e23bd17ef1910d75d29c2791886", + "0x2a9ce5a8366862e7b790d865b0406d9d5d4013999f42caa9235d4c2b2813449a", + "0x6c67c00db439fbc9d548cbdb57377064419ac47211be90747c74c13b29d4e562", + "0x1695a7ad7e64d438a9b749b07180910a7af207bb46063a92d41be315cecfda67", + "0x6fcb36c8b8ae4d6e4bc21fd14e80dcb402561a011fb4e71408f05807b1141f49", + "0xf6ff3dd9e40b6bdf881654e12fc9959a928bb7d83a81189db1520bec70386ec8", + "0xf17f8b88f04772598eb87c8bd1b3a03f5d96ac68de7d36be6c1fbe5fd4a2ef4c", + "0xf5d9c9e25075f8ab6c34e9231d36becfe90df0ae6bece4ad1847dc4ea938ede3", + "0x5d409a230ffc6a7e149e94b0184bcd00eb8705fe173c67ccf4f81e2f0d3cb0a2", + "0x80647dea82a0c218462836c14cab2550f558391a901a708d7e4e6c28b4b71869", + "0x4ab906af7910a57f1f4bc338d5e952a899ed256e2a28e1947890a372aa29814d", + "0x6e6e180aac3ebd616af90069674ce54459f169d33d30d95d98351259d90a38ee", + "0x5d30699b02a45d5cafc14c945bbd208943ea83ac6a10229c199a92b87dc0c76f", + "0x19c26fe48c497a4b64a4d537eea9646cab7704dc28c46fc3510a14b226b9a690", + "0x5ddc881ec8fd1bd34601f06a5ae0cd23680e2e8bf12b78df48a5f86ed6e96ab1", + "0x39b933493defd81502420663a0c85df3a6cf727e4270816bb3cdec3dc58bc7ef", + "0x62a9e911426f267561f84210d7356069f6168c4e7afb9aca4a4a70d18fb34ba1", + "0xae792d0011eeabbae89f6de39ad44f0620e50e5ffe7392f9bb792a966d1f83f7", + "0x737b257fc55f1da44584687b3cca4bffda13ae014f089e75f0c75b0b2d08c426", + "0xe923546185355abaade908d18f3223bb9c404c4a399ff94419c3a5798c90d9ef", + "0xfbbd3e2dfe2e9e6f985d139dbcfa5b95da918acbf49de992d01875bd44b61e28", + "0x781d9962f2cbe94e355b93e4c0eb3a3de44d9779eabdc3c6d18e7120b3c0c01d", + "0x2722229fd2cef3564c38d8e4120a6ae89728ab4063c8199ec098f139b83ee0f2", + "0xe29866bef5051041d25449f334e4ca246df606c5d7c0bce7067c6c98cafd0534", + "0xe0320de1a972b563113eddbecfc90fe902f2ca80befe15378c94bd073b44eb92", + "0x9227e1d6c68bdd60cd0deb8ed760eee67af6623c44d05136251d89654f3f2e7d", + "0xfc3c7dd2d36ca954545f3a978cbd7e4c413ae38a0fb5cb59b84329b5f5ff2347", + "0x934c332cb52521de4216c29e40601d3250078fe9314a364064692daeb38f71a6", + "0x77aa1b31191caaf80acdf5d411c4b6b834a6e50f2f0ceb17e1f06a807e3eef01", + "0x43eb0928a0ab9a29ed084cbf9ae75d1e5593595d55fc8a32151bf56b9977a1dd", + "0xc6f4fe473048728c5031a465c9157e38c72367c7f937aa9633768b32839e765b", + "0x21dd6e252382965adf71b94fc0b9b2e6e5b0b9dc9732987e1fe8d8610b1125f9", + "0x9808b8c9e07babfb30c120a4a7d3582cf4259f8309e586ad42bdb9e9cdd3dbb4", + "0x72ec40a055dd6c5f21333f9540bbe7e38f28f39f90204f6f87f3c94faebabeb2", + "0x3a04743cd849a1a48e0b012f39fa4eb690c314eae7b0500ff1ea487a6d399734", + "0x484892e8d3585d871f7105a49cdda18af036d787b6519f9d32f8dac5a3eb9546", + "0x3a73f64acae91b70d39cb20c1ace35547e733b253b47339a1c47ad0841c7a27f", + "0xf33014797bb0e339f2f0dbcda6706dc6b33c8491cb9ef777ee2cb88b8fd1e35a", + "0xf94ec9b178e1e287fc2aa53d397960e89485ad22f306bdf0226212415900648f", + "0x8aac6aad1127202909e112df54be070e353fb9079dc5cbeac146ed09faabd4dc", + "0xb8fdae091491634efcb9b2f78f1258d401a9439d806fac04de4a0f9503d9259f", + "0x4e872ddaeda3ae79efbdfd3ad8e5ac3597003a933aa793c50fe74fb59ef43637", + "0x15293110604e842775b59198cb8dafbdf07b4069fa0c3e2d61ce6665ec8f6742", + "0x7423d6d010ca3aef37831e003164b583c2cde4e859705ea191e6ec687f08cc49", + "0x73828a90a0f97e5bd83c4a6484f84ef8af04d325b739a2cfb96b8cad36717b09", + "0xc9af1d054e4ba3af1002ff0ed63fc361be99f8c7bbe90fc951357ba28fafb8c2", + "0xff2849e97d79e7c25a58266dfeab64f512ebc57620d45d6f5106a71fa43063ec", + "0x98da87856c0aae3af6dece2efbe0a2f7bae295ece2b90b633ff894e5c83fd407", + "0xa17f5aec76c8f4aac84741baae18e179ce5c1ce0d23d3e2e5b4fc7f79ee91171", + "0x79c9a766323f5381dce7241fa53da4c7fa018a43556352c54b52fca209f3ee37", + "0xeccb07e66f967613c13a1e867f8a96bb2c96b7c0347e4262ebdb583eca48e71b", + "0x36c357f0249aff34dc519f420b77fe14357a009022262cca7f1467c2dff7d0cc", + "0xd52a496e2c176cd98b94e8dd4c18a9651f46ce47d70c51b21e5970bc7d81c96e", + "0xd3fbff9d80c163f6b9d4ad4660f4504101e167ba866266ff905f8564ca7ffb68", + "0x321b832e81f461e871bd05e6847f8548825464425036a1a92bc44fc30961fab1", + "0xb58be0aa640c7047e2b27c6044dfc44c216b4b69f48751686cf29e66a8dc985f", + "0x2c9153f20a845b8ec8e70cb1dba8436c7af618367c3344130defb1e7c39afe1d", + "0x6d129dba3e7c6e5ba8412d9290d7dcbd29b36f3e93eee6b6d203740de06802ca", + "0x5f8573d1de1c9b4725fb639b565a2ab6dc79d138c3d6495325e6ee609073965c", + "0x5f78ebcd3f73da424734bb2ba0c711fc54be4f21f29afb1c33af44c598d31dd1", + "0x6b48dbe0e509dfed74f60e16782d268076e53863b9f86e4078c90f4c6619b644", + "0xec30ba6ff376fc1ce9f4b87693e586b5fef2cc5a5ef26726d8e756b3c8e28920", + "0x8ac0cfa126e35c8dd715bb0ce13bc70c8d55c34be0abcb85576393a460762659", + "0x8825a63c211a92ba8f8f5aca7a27d1921df071072467c88ec9932a41d43ab5bf", + "0x39cf82416d543f45afcc6aa0cf68522d0d5ed4f76b07b9f742594a88309b393f", + "0x105449b542eb5ab0b1ec7ce69c5e8bed9a72b6470b644e0815f15c6d9122663f", + "0x10fd8c442d6d44b533675b937e5defb004271a87d4ca3b238a395002da42adf9", + "0x390a40177f4155c30bc10539a54991f4759ecea69c31e9b2904743834bbbc618", + "0x0f11e934cc159af39a4b22bcd6817b1abc21bc6fe81c442dece1c178d695787d", + "0xcfeb9c70d8e2d4e40bca485200c0b5db1152d9440237f76a58d4cebaf43cb1b9", + "0xc45c5247fcea2312642437f90870b6fb4b9927eb2af3adda30b5c738eefaa952", + "0xb6886cd5b6329ca5f5e36d1c49b7426b1a20d3e3455f660e662128193b6e1a32", + "0xb708efc6f2f6ab093482d8b53483a3d8216043a437da09433ad0eccd4c9d607b", + "0x12b76069143a66c55fa3ba54a27e2cf51a94d8300a6a6a317a65df345a9d77f8", + "0x0d9b6dacca16c1be986db368dc92d652984da9aad149ba7c22831ac94719139b", + "0xbe298f54460f273d2106a102dfda3e86d7eed783bac4ab88d8caadc0f3116ab0", + "0x82fbddaa5cf8847d2166d40f9fef5fad4933bf338422bc5457e5d5c29774e5b3", + "0x2a75f2442f4ed494666f48168ebe8620819d97f9749b279eafa550f20b1b622c", + "0xc874be785abc89e0fa92d089889ce06e1b2297f647c31cddd24d29017ba74b0c", + "0xa6984ff3e4f7ab1da131668f77342bb0182cd23593dded5e487c134b1b13a9cb", + "0x4293f4fb5bbd41aca9fb12f58e2427e20b791ea523fa125c40b72699ed89ce02", + "0xc316b1b066bf437d3933d7fbc8a8546a10f7c274d2ea84781d8a99633bdc0c3f", + "0xb40cf7adca885ffa6148d8008c5df235e938659d6249fd883960b847a409747c", + "0x3285b34b3e90a83293292221a89bb244a91ddf4647cf2d297c2116826c605640", + "0x465af97dbd1276496387568d41ec69961f70b382c6e0686c103a73bccf0f7830", + "0xda2cd161e715ffd3ca0eab361e66e55450230f9acddf8ee635b412807a9067d5", + "0x3136630cc6b24f59ba9c0347c6c612feb043f9fb58d1d26598331aa623fcee68", + "0x24014d00bd6ed71da4044cbbf256ef99927950736f084cba2a61046ed7c8d471", + "0xcf1824f9a407009243ade6df4e3f241dcb60bd7257d843792d12fcd42e659076", + "0xcfafe5abf154f2ca9b6f9d70a91149b01cc8fb6b8bd083e24559b860e439f04b", + "0xa4de7f7024d405f6738301df2132107b135ecf824777f1b844685bfaee6b30e7", + "0x507abab909bf25bd3907ff757a69acca54af0c21ed6faa975d20106908e8540f", + "0xd6e584b876926c3989b84151eb574d737bfd229151dbb0f653b693b2bac7c45b", + "0xed4eedd2952e96c9298f68a1f8ffc4772652517356ed4a02762a3355e7dc89f7", + "0x36d0a7f0f5ebf3c35a576ff56c8aaa1caa04dda95edda5d496381c9601949451", + "0x9beca310b79181f7695a0b6b49e923e7b67462904457df0af82c88b92fe4ac3b", + "0xcf792a534a0b5126116e9f7c9b92aa4d32083fbdb539c91dcb8de347000b49f5", + "0xd4024c9ca180adef5a9311c8a2fb0fe75e64728af00ca4ea1484548f182b5488", + "0x5517e825a0d6e1bd9ab4435278b8691ad2772b9edb581b58725483c4393187f4", + "0x9e4ae4c36d00217af787ee52862f87bd37cf391adb23a8b51fc05ed1ec3f6631", + "0xe428dd2523abc537b868bb6f5588597175a622767b600b1ed9486d09624c8ab1", + "0x2fb95198976ddfea942f737322470f9de6635f7116839e5148d3f64766f6fa2a", + "0x211ff8c237bba495c03bd6e8eea2a64f05c8a0eb15951751cfcc4c337f1cbd02", + "0x44c49c62604c39e38561c7d9285b9a2e168fa164a47c7b17f1fe83faaa3c581a", + "0xfe663bd908aa64290ccddcc300aa5f616455d8f10032af9317ddf717c94d0bd8", + "0xb04add424a6780e81e2cdafbd81699fc5ad621c666e1ab5583f3953ce8b3abdf", + "0x5f2f6958e89ad99e28e96059aa8b6e2d9518009f824874e327dbce226918e0a5", + "0x491c9e17210c2770c0cbe566aced7dcfcb6781e70a288f91d4eaac8dc4fa07a0", + "0x73651853f625c534d5848d1bf7f93433b09d9297b13d46aed9b022ddc37c4b85", + "0xb1333ff43af73637e2235f97a1a7a5f42c590cb15f93a733cbe16d1a9c187e0e", + "0xddca0492f5c36c8eba140575b4521227ad1216d04a7e60da7c9727cb2a756df3", + "0x6da48a25ef31b1e25e33b1df1c539b14e6d3837f8061c6a4faeccc861b7c908d", + "0x7d250fc602fefae7409a7edb1260f4c520d4717b881c8afb6551a89cc781efc9", + "0x043db87fb979042ae7ea709cebe4ad58028916f035e99382be9826f1236e5753", + "0x01b125ca25e2a254e0ce54e4f3da04c647ff6fe982f1df158c3caa90b1e2a2b6", + "0xec617f330285f2d4c6ad161250688cbe57bb81a5e45fb11354ac033e2fd724cf", + "0xf7cc72b3307f0ed731b990d2c10f71cf195b02f7e1d2767873c3ffc192cc43ae", + "0xf19619c5e62c5f244ef4156cc2c8f6edb6c230664e4c19ab32e1fa137c9ae173", + "0x91c313a770e9eeb92fa74c335f9cdb735bd14582b385d2a380bca534cf7a6dbe", + "0x14c1ecb6b06c18c240932c705154f009d9032dfe576ddb545a3a4cfab9968fc4", + "0x7d2cf6029c764b4c676c3a30567efa13f0fdd3cd6587631bd79cc08f26d75505", + "0x8c889efbf596d20043d654a6cda1a58d480ed2e3c3db65f51aabfd25f85784fc", + "0xe0bcdec3debf12856f3b4fc781d407a52b6b3d544b56cddf847105760a99775e", + "0x32c229b6b99b2173338bd7615a1a0c2c4fb5acafd41e332c9be7dba91b8e89dd", + "0xb24b187ffb16385b85e6782fcb4563309afefa8a7900cfdb81d024c2257913fc", + "0x81bc38df942c4c9c309f19e4fe5119f3d7fc2347fceb83f7d31e990a042581c0", + "0x1e6ec7caf14747f470d65bea046f7cc03e081271775be615905632ec9a18a637", + "0x4eea8052ac23fae83fd1fa63b328bb4c2c66277480b78ecc00132c1afa53d37c", + "0x8810dfc2084515339a0c6f1b3fa771aa0819b47498e57b3733aed0d40b9a55e5", + "0x3cefd28b10c2ada59c1bb66a832f1ce79c6d96dc0eae3b2759515839cad25ce0", + "0x8fb0cd31102c06d1e33f782fd9dd7f6f9af8e975e10fbe560183ca366d088039", + "0xbd7842672dba588021f7b395a55b949d41090039b14741ac667aa75977e00d62", + "0x7c4ceca9b53e12dcf2bd5a625274f30e2a825d5075d640c7bf78abe4bdab907a", + "0x945f8fd6e3171159a57fe5ad95e46cf93c494613247051b941bf4ee7fd6d4e29", + "0x01e9b5d3ec0f6f100907603f591e5007d086086476dcdca849996b98089b924a", + "0xec9545ab618538789965fd828fa8830ac17ee52489dcbbd71b296abcf12c11ba", + "0xdb1466a3705b6a035fc7794fafb497fd2e7606b003e73c6cae16b8d9b2aed314", + "0xdc36ec38da93a79b5a1e70d1cb3453f3ff324793d2fe64fc1c4e8fb1b22e7f29", + "0xc1f2a76b96cc31541362b5d6ae9271740bd540631b4f3f0274ecc5928e17e10b", + "0x4a5108aa8e22f9555d94caa9c29b9793b4a97e2c7270b8e24b2b3717d9efd8f1", + "0xc8938a2890c4f884837acb06c4a3854d1c03286d58d5d62c85c06e00d8597fe9", + "0x61e1708488fca9b334a1682d4f11bb046b5088151a7333655c733766ba0e5e5d", + "0x75705cfc2a41a44eada0a51ad75eb93972d84122658c487803287235c0a5a405", + "0xd818c5f5dcb79e84b2b41341261bf3e4e97f1bcbe572c1eb5be5f7e3eaa52db6", + "0xee9b8a62f9bb12306b38c129ddca08e1481db588b6e07b45a7defc3750cec078", + "0x1bf73759029c056f0e59b27a8d05714c916daf36517080e724662bffebf9e380", + "0x4ad981191be35ab37c559020fd42db86f57c56f51a902017aad1deb2d71debb8", + "0xea76176e1ae162188a798fc6ba2426c5e39b98a2832ef5f730c06d51f0b6d71f", + "0x1e425a410f888435fe7c1d6912efddf8ddc3a763a136ec730c31ca3ed0a3c162", + "0x5f6ba29d31dcbaa0220a7c2f901b987e968233bf19f398528e91973b421aacf6", + "0x935266f8976c34566a762204de924295a5203630aac2d2bc9c9cd0cc8286819e", + "0x323fa129d881b6406662cf69f01c720060a13cb7c93ea7fca3f089cf47d1b9c5", + "0xe17499346fcb6ac5f95e10c794f05ad12ba85d201c4d9a2571837b287dd90fb1", + "0x8bd288a3aa8be55687979fa9d94ef1d1ef8a0036bfc5615419920c6df3c31bc3", + "0xf05028fd3368402e82d4f6c6a4b12e622b048a612f80567d6e874541998b87b7", + "0x7e35b77cce743749b57b7614bf4f3724226ea82cc1f76e6f6232ef25b42eb6e3", + "0x58cb381e320b2ee7ebf17aba08316d86c6e8f2dc038f8fc6839e94f16b411a3c", + "0x24c1e91b745127f12bd8603ca59bacde6fe1b990a1d4de58c5e91c29da27e371", + "0xffb99ea8a8fc58a1c7e33ffcc199519165a452ed9b87196cbfb48ca23198e571", + "0xce37cc2df44d4308d1e5b846d46c9d7f2bd78f6b771b1eebcda1142858cbd4e9", + "0x0babd6dfc4f5a3da90ffd6f74297db7ecc7964f23bfdd3404f49c47cf7019856", + "0x97ba6463ece697d1b340dbea1e4e51e2f59e1f1283fef71d2aef12060b188b6c", + "0x3cb4d7dd238435f316ef70fee4bda79bce6b9a333bc10a36c2e58f7fa4bc24de", + "0xe44502680a574938a0f483c72cf5843ffc506bd2d9eae270bd108a4b7dd55422", + "0x3c9ef69fbc9a23ff7646a9d99f27584f3969b20bc56249dddc7f83fef68742b9", + "0x555a8316eb846760d2996998291e81d5f80bb0d8db71e5c02c42791ce83e456d", + "0xd4d68b9b231fa41e4571e258fb5d4770e843b3e50832aee6c7e327a568c062b7", + "0xe0a8bef3a6755a4d50f379f9f324f0404fe677372f307fc67ef2b938eedf986b", + "0x57f32b8c263c9fa6e3b5df8603307216469dff93a400290cc54f3a76b54a2a75", + "0x3749d954f6fd6dd710a0a9b4abcb0da071b3c50d125206c0caf4f4786d590d4d", + "0x58efd182fb42171c896accf391b30f1916503f616102251b9dccf2a45a823c5f", + "0xe44810336515f4f45693cbc57ad0eff2105d70faca32097f7ad6aa505c4d3b67", + "0x0d720fe2f29af2607ee5582016bd0f122f63d0f82a84c4931446a184843594dd", + "0xb3106d35e5a904b6d460ffa8cb494f6a95045d717b3f532847105bad0dc5ecc6", + "0xd1bcc88ff9f5ed1b7625e7242ecddb2b615401dd8fcd01c9b9f03a6d22d144cd", + "0xd4d4702668cc3673bd4266be291aa0f25e8ff835df3c43d556df01342986a279", + "0x9fb62301f00d441319f81de73fd49fa1126a29b3a11b022b3a92401dcea15772", + "0xdac3eda26ad4eda3d83c0e83d65c74ca4c4c3ae47eb4e5e82c6b315f80266125", + "0x7b24a259081a547d55e9af822fc54d5dc6f69ee3e961ebd0a0248ac36f6e25dc", + "0x64e96157f5e4be77c9f50df8ea9c518026ce1d7db6adc52189c9354922504635", + "0x35ee981b1a7d307671605b25399cad71503fc9ffc00d2f9ca80e868901e9c269", + "0x6965520591a7697f7c62ce4aaa6bd90691ae1a89a5f8e29237ce5241d8003636", + "0x30c305cc886b538fb8808be71bddeaec3a1276986906b7f028347511bbb653e0", + "0x5f63caf0aba563f62811903619a10786761011d2fa6fb58e835b8dcd5cbcb3ff", + "0x6d7219f08e8288e4dad53a7fe227951e81d9ff2633bbfa636a3d0b147b98342d", + "0xef315cb919bc5537bdc9bc47d2dda7442e4a372c74163fc2134173773ebfd7a6", + "0x83016cc642eb5b55b5a667d36acccc35eb9c33cec2caa28ec263f00800bca2c1", + "0x0c855426475046350b58fb7e179202cd794f482598d73ad05bb0072774561493", + "0xcc8dcd45a49fa55fdd2871245cbc611613f203203ae4ffdb1e647e7143b8513b", + "0xefdb25f8a05ae4c869bbfbcac9c302f3dcd8a917eca4d5807deb09833e876820", + "0x055646d6adfd108be923b275e21c07162227694df780512d0dbd7a0b58cdda91", + "0x4abdf1b201aaaf4724e81ff29e8d48a223f7b98ed6d9c9696375047b7643280b", + "0x929ef44a9c004029274bc1dbe13b49bd6452ea3a3ee6d378dc296c9baf0b1e9b", + "0x909a57470dee2764e5ca646d6711de3908b3b35d98e73f67adc23a3a8aa3c48b", + "0x42e5bfa041faf65f224d7985ad464a526a18fe591a156c0b70df9d397beb9aa3", + "0x241d0b24f3c0e08a17f10d579e49e72af7d2e4d0f0d621ea8975d50a3901950c", + "0xb1f3ce2968fed1bfcf363a031d79cae0174d642e931862adff7ee97f947522ed", + "0x7e4db35d3aaf0bdba5cf1d5465f77e6c927f42b775e8ec2bbfb43be36270b5b7", + "0x7995fbf9d69f7222ddb9d28bacc6d97c61780755e933d3dd47998f1f3747aa32", + "0x31317d422307751b59eeb45270518af357bcbf7ef413f81c8e7c6bd48d79d696", + "0x5c58fe54e2781cf91a9d09e080a9ae5b6a8124cc06275aea65ffe422567aca2d", + "0xa4da628ada51d57b378717218e6753fda428e2b8ce88859f37837b4c2fcc5076", + "0x9c9f744e7e3275808f9913767657af8a33a7717e45adfd84305cfbcce328a065", + "0x1381182d1b1ecd75969148742706a27a044cdeeb5cd984886945e5de5f3311d5", + "0x71a4759a9096d67a4061ad639ea4ff7df35c811eb1c7b10638aac889584567f2", + "0x1bf9536eb1692291213d4f7f340647025c10793c2c84e8e775db5cb5afb5022f", + "0xf2af02ebd1cf78bf30a215d80133c7d5f7361a58b827efa23e940e7c91430af0", + "0xa90ab1a925b660baff138369712be90a6cf8ebb658ccccaa9e8b0305adcb4ab0", + "0xbcaff27b022e0873b10da43c78b1395f1878e2ab1c8cf33368988a6060c6132f", + "0x6154955bb4a81d1b96e618ab29a53c393ec78153a733f90227c9f83683131eeb", + "0x451b4a606ef40653e1ccd38b07fd4e8efd9fe796e01b5a5772ff903b45042187", + "0x011e748c7fbb39346bba2b3235c185fde946d28627e2f6ed39c68076d5e6fcec", + "0xa849d075319c4f0cb13f3115ab99e0cdf0341d719f5c65a36b100ac1bec98826", + "0x1e61220e9a615b1ec127001aaa19f307c268bfbf14bca900ba1005539a7ced1d", + "0xebc6254608585c487baf6e312130a06468551cf4ea171335480881f42ec626a7", + "0xce073708172f6fd1e6d792f4b9fe088dc06e164422c5e85e4098c0c8672bdecd", + "0x8102653797d93a7d372b1ddbd92c8c8cebf79fdc8213392668d62181c2639a48", + "0x2062a79855e50c96f3eceae62ab0f873eb3f051f66931efdb00649f07b82061f", + "0x28302f6a2106296e587b03f2f0963ae9c8f8eca56198f7d5e0fb52d4485e1da0", + "0xdb69e062fd4f03ad8327c3b33fce7efdd2a1455cb9c1c90a7e2e9127b12ca041", + "0x984232b5675edfe8312be559135281ff49be1219e3ab17e84560221368166aae", + "0x303557f3d60ebeaa591cf88e583c366a7db4adf1031b3de9c8d3381466e15b7a", + "0x2179d8b03104be3b8d2e6f4ab4c6b9851bf85ed4545aee93fc40d3320d3b5d2c", + "0x708174830261838f9d87279a51c4d65814f7da2495c1a7ba1eb867ae7217bdce", + "0x5c5b77681b0d9d6789aea3494c60571311f2b6690de38c2e4c2a8caa738afe5b", + "0x68782d0db788b3e7441e94e4300a93c50d641c060677c6ac98ac50c69ee972ec", + "0x3c3acde8f5a99d8e131d33c7d55ed4415f0c230ed6fb711627b11e464faa3264", + "0x0289fabcc6d32e03cf75d7958e22ae050c8fa6b31c03221d78ddab3469d9b95f", + "0xf10cc55e619ba8b9aa5ab92c272ed1459020ae6f43e45fd9d427075d86230c36", + "0x20ae1b4f3c79618339a0f0627919ab94809328164ec1dfa6b9e1f556e68ce770", + "0x071e6f74436830ace1c6bca63d915218d8f561e184d15cd83ddb199319907dad", + "0x7334a6ac9991ef0876f0b4bbe18dfc407e748c65ee79bcf114c1a2a71b46f41a", + "0x4f44e709a1e59bfc7315843f399add75d5c1e8c3cbd0f485afee94e1c4cb0d33", + "0xec0514297634ea605be68932d68de290e15306061e20a16b2e3bf675fe573bc8", + "0x9c72bdae94b6f57b594c11872ee21af41c704ba1addfaf8ec4d828d6ac6101a7", + "0x1d1a60f27879b14930bb0351e39d3d31233e7d55f3ecae5c9cad4affdd2edb67", + "0xff4fe06f62b19f1e6079955baafbcf5b74627785b5edf90cc49ac75490a07f8b", + "0xcfc332e8af474ce72548c5f0fd50d4e6c10858376b0ddfb706e99703177b31e5", + "0xd7d5eb515bad41cf09df99d5fe0400ecab66ebcc9f7af53ff8cad256d9ad2bea", + "0xe6f7024457d9e945da6737c34090f689bbaacd479849a7c64afb6aee85b6950f", + "0x15fff05c7ff12fb455b77b5430532011748a52abce46919f69ee0a4c30b59fd0", + "0xaccd46e8d8482209373407310b988ba3e1f39d6eb17f07861d33bfaefeab3347", + "0xd97650055032b7bdbabc7b49b7343d0362d6241aa45fb17ce9ca1815e7d58983", + "0x0a4b45c31d3a1dab81b47da37e896360fe5a1e45903f59d6ad0f0b8ceb2a7747", + "0x8b2a42405ceba8740c02c526023247a262088e35588fa4253eb6e229c273463f", + "0xf43516578e7fd03970d57b08384b0bb3441ee7f50213c2ac0d1830d43c857abb", + "0xe2aa1bc333310c27fd699455b06398679a100f6a942c0eb37f70da89d7f025f7", + "0x1f13aa876a2a348d7de6adaf51e8af65ad7bfe0332c4c9cbb4dd709dbde8e1a2", + "0xd766018ae514f5b7daddf6e716bf4570456cdd46e7a3be68c1c515f2df441bf3", + "0xa2ce855ff00054aea57559662ac95b2445c1fa2491d9f4448183a474396daa53", + "0x8583ceb1254f6546539fdd57d1fa1e3580d81f1aa44dbc875de8b0f56e5e7ea5", + "0x7e09579f21572f2ee72399c3ef67c01a5f3f27ee4a3c43e8b6f692e231fdcf1d", + "0x6153e318c7eb123db88f9afbe8ff1c8f66e1cc0e8704db45730912a8281b3366", + "0xd31dc325333526bfcfd29e50e5fe13cd696e5948910abf46e66dc73f7d271942", + "0x9089fab0aa038217485edcae76bab74735a1fd2897c30a610f5e43f1d2da5425", + "0xe193e06c904f53031be5910209b7aab785bca6b65e917351764e2f231800bc4c", + "0xf732414a066d5a8fd13241f5aaa9570e5c6285119a1f7c69063a8b4cb62729ff", + "0xe149ce7e516f9d54df933717a11040e4a9bbf8ce13c5a3612288009a99724295", + "0xf29212544b867ec2505e35cc5ce79ede41fcafd676f41df15af7f178d6dc5cd7", + "0xcdbad6c9eab35d13dc6461ffaa793b180e28085b8047cc56a5328f4066ca944e", + "0x13f5597439e924b31cc8ab8b9cce0776a8d89bdf7e6a0a306763ae259bb92787", + "0x791e0daad106b9ba1ad684994744d2ce89c6e413c51954321d9eb3d87e0ad14e", + "0x892436f41d7a6d8385d66f6ff19fc7235dc6241477d2745792bf3a38085c8d5b", + "0x68b0efdb41ac9e0c2cf0df78ff6f9f790c3c1ba63ecec6bdf9426d860952f81f", + "0xcb4cca4320ab1a4ff11f868d769ce9fbae1e98d154207acc85acf2f8dab8c4f4", + "0x3dd2dc9e0d99e9ff820f22283ccae495545ff37b6e7e89b3d721bdb5ee169eb8", + "0xad216b8e962ca5b8f388c89bdca543059065c7b6573c5c0868be031b74c2e184", + "0x36953abc02bfd8d72b7667b068f63f6c441e78938b15591440a9550ef796b8c8", + "0xbf85fd9301cb248c0e616e4297e2d4ec43783bde218ab67a7e2049059236f633", + "0xd6764c6e1f3e8d671f1cfc1e55c4d65b274fccdf783ab1b27eab1099b9f46299", + "0xe1f4b5fc4fa5e50ea8a7129f2786d7b13531d608ad6e413d3acd0637ddbfc5ae", + "0x225f99906c6ffebd2c77eda9c7b2e318740a36f84b8d6424b2f479f3e98b1fed", + "0x7850e352fda7e9b524fb1b1c04ee4695ba3613441ce0c5321b26b9cdb3317625", + "0x4913a582ff57cb276667a4eef0e85d31c79ff5f048b8b017dc89653fece5e88e", + "0x1cf357321372b71199a20da7e7490bafa5d9f837fdd3e433a68e8f84d03fa550", + "0x978cd04d02821dc54a1ae97385ca618a91ab42e9c227b5da2be433369825aff4", + "0xef0098b9b604b3bf4b7898d887b5f36ba8c196606b5688476547e89981e84a21", + "0x1c9cf750c7a5188cd346781612291b20ab07039831a77e55ed890987a876a412", + "0x6bd6e5e5f57b23bab83994c68760951a0a5126976fa9c46a99bef5b329ec7cc6", + "0x14db2e7acd6fccb3b019382385a1f988dc368d5da69cb96956901bc4ce06ec1d", + "0x48c20e5365cd6febb128db3fb93ca7a88b7f2cd3dfc5b7a6fda331e62d8f6875", + "0xec6ae4cd53c6c40f3b77e82cbf386d478120145c0c18f26220eeeefccc54b883", + "0xb27738db32dcb1fb5637ee6b86c528aa9ad75a47777931d45285f0745d6d9618", + "0xfe9671bdffedf2170714da275ff0dfdd533ba1be2ce2590bc1ebdea9208bc951", + "0x25cbe60b7936b87c09e92b9982cfd1bf80ee1d6ce65133434ac330065c9a60c1", + "0xc777ee866895e41d8e7320601a8341fcd99c98ba9d600888397f1788ed79fce9", + "0x63425e84adaa76af6e58807d3fc3dc294e9bd9c32a170a60d8571c667ee804bd", + "0x086c8a54c5d1c2f4160d7f0f5f6c1a333ca91875ba23727025d77225e2f4db95", + "0xd8da17ce51dc8419c40723d1c3a88f74fe336f9a4f98eca61bf1349b0d4a89da", + "0x0d2169c32328187125474e97c7a917253b90dfecec0682f3ca7982e064fffdf7", + "0xc83f335b6903eb3ad4b0f94fd6bb3a8b5b463d0137547a4252043d58ac00df75", + "0x175f57c174875ebc474879251e4bca53bb7efa9f27f456f10266864719e073e2", + "0xb0ea4c419abf7a74ed69807b3d14e0de6def5f55e645fbf0a625c7e9e3765201", + "0x4a246d6ec272afa613f9b6aa288477795bceb920d6b3ed1a78286314cccc2e35", + "0xd3af9fcf65dce23d27b46704753c0b4b24dc752db5389876d31181313cf91ec2", + "0x7301d3627cd3bc2ab317c377e4f81ef6b07ba64939813b20050b53c8f63a533c", + "0xcf785d9ca7c95247ae005537d0db37ae9c54494b0a2c1e655e9c5ee1292e8338", + "0x3de4a30d38f8ef549ae45b777149a547c8cc785c6e6f48e5dba40b7bc7fb4c82", + "0x27fbff002680b942c31cc8f04211be03c8646f024857ae52121dec778163452f", + "0x2a37be7d47abffbfd08f1aff379a27e1d9dd79833f67b466a59b8ebad854e3ea", + "0xedf73b72736afb79d07c6e726c74b88039da0c21285ae55b5e2c8191d3f802f0", + "0x99a6e045680aaa9aa3c175140a9337aee7967d78e97b41a0cb37c03f60c24b63", + "0x5baf77fcc6542689195c10fedca877c6d59ec418867f25c79d4c3ffd36493b84", + "0x85d64816bc21349e07c5dc852a8790c6169d26274dc4a7fc35c53c199c8f6dff", + "0xb59cca680fc9baee6d3d80e11e374bfe8c111b0f47be83157e54e6d18ca93e02", + "0xae13047af776510ee9d13cd6af0e669495704651d5894f30e5e49c6c9d33bd0c", + "0xb2cc9c6705f969f07f700e3d9fc62fbd9de299ca3510985ae5e6fe339e187c75", + "0xb198d0787de7b8e5e9beae4821f587608770fab12845a901edb33da4c50dd217", + "0x7fec95f60944c1796bf42a978a3a771613b889fbb14e6cfcaad143819bafff9b", + "0xda81d0a99cb64b0c8828a05f11bbe38719cd6ddd6cdbdc814a317cee492b1503", + "0x554823c8958d8631a06ea110de162710e66da426210c4c89bb210684c7dd9394", + "0x0914908175ce4d9fb7415d5fdb52367c616a1b95198c9421459cc1c769911cc2", + "0xfbb629523797ff98da78d0f41e2acc45acbb20e4a1e0e1dacbce45c69216a964", + "0xd88684c269bdced43c0bccbf8c041aa6fa2fa9354a6aa67d7053aaa0f2074962", + "0x3920c4c386860756180fb1f7fe605801f7696d5b9e4e1576e1aae0e8525df880", + "0x5deb5c080a346877454cd98ee36312bc3e4a79b3cd9f3c39971d892ed955e927", + "0xe099ae7dfa63b764ef3952ef921cd229acead0b0e047af94030ae09a34b0be2a", + "0x7e754b454ded479b4bf36835b0d52074183c9eb6c42398d3e7338948992d08bc", + "0x1b22378457b71df9263473eceba4f57933f11d33d31be11ec5edcb36df0dfd95", + "0x6605794adab619d57a2ad55913afe54cd5ec219f3d6740185409ac61d32a2567", + "0xa1039d1a4bb3e763dae6dca5b8c00deac9bf0b76c178eff53527a95852c587c0", + "0x9d43d1daf7c4295431136143ae7ad2192b71c16e8da4cd1ae9a282d113122c6f", + "0x38d5c14dc112d21963208fb735e8f76d9842c2ad13e2cdf32b0f6e70006f911c", + "0x09396c809912d9403208968d6f13d8845398b26e93f444acf7538e5c357df7b3", + "0x1ed6f3b995bfbabb0d55b107b2b3ec11c4be5cd68f207d9d059a8eb7671ccb8a", + "0x69db9592c01441747056fc5c646057d4bb992e4affd23654af7f24c47a002900", + "0xef015d193b0137175d8de710c2141d23cae1130c368a20f19bd59f9289d1a17b", + "0xeb63f8ea306392d336b2a8e0568e1a067d8f40c4e2b28f7911ebe48a67638942", + "0xfb12bbb145b5dc7ead7ad984045e62b922649a70ec00da06ebd9eba65fc83c23", + "0x10bdd7c613ce8584ac10f75891afefacc98cb0c711504e0f0284f634d19c3714", + "0xab851b2b35fcf427bd0407865a5989c533b855e65a9891e39b2a5e161a345af1", + "0x6bce48ac8c3553878ff831b672de737cfc7d7c3293e86fe322d229ea3f5d2c78", + "0x0c7313d927f845de9f7c03c09775e2c8fb8e9f2fe63413fff066e9f38d799ed0", + "0x347135f2e53f4002b5687d287b5134de9134eeef4cfa4d4494b086d957157870", + "0xedde43ae708664ffeb9d92cbeb03daa5fc0c7d1d03ac3e9d6541dabe9ac50c65", + "0xd14aabe2a11d404dcbabfbbd8d7c73ec05af5a0f9fcb79bc8f198f00420a2179", + "0x2c9d2e1c881d46c0fd68fffbd5ac45e3dbde2a4d1ddbb77d0ddc4f520ab83c94", + "0x42ea8109042da88b6ed9c71dca34907f54c356b46db55955e3a6cc1541904a0c", + "0xf81c890a5e8660bee4d5a0f18cc0f3a709ce48a5a49a36f0138ab6431783263c", + "0x492144091be2113571cf9cc84c53390fd00db10eafa2f5533f78b8a5d6bdc7b8", + "0xb42ff2a6b3d1e736c56a37a0a46f927caa4ba9fa6bd3f2d1a98459c7c30eb262", + "0x464c4d7daf1acf7786041d3c797f15354cc03cc5620454abdd88679659df1292", + "0xd5dca22e9c436660ee18a941129578021359c0bf2f22008b161c91494ed80594", + "0xb54d6f7ab969f0f95ca94d0a2070b5f2aca9f991e3b64a60c903a03d3d48e938", + "0x1261a3e911769c5c55151982e994aefd4d773fd96e8583f41a44cda306b4e1ab", + "0x669a364ae954397361f4c703c102513315552af241e306406fafb3dc70db8ec9", + "0x54d5a3162659886b7a0aad65839ef2cb72233dab58bb91961d6b6ccb1476da8d", + "0x449242a1b0ecd0bcfe9acdfb2b6779c9f291c98c09add8e13981617486423bbb", + "0x4dda9dcd34dbf574f62d475b7e3052b2e393be6331ab4b0ddb3f1a6270041e8d", + "0xd23f7682f463c5f58940bd199136afba80496fab562aaf665710ff1d5cddee66", + "0x70503762d711f0ed6eac70a12b728f3d84152094b1e4f9495870433592cfa268", + "0xcba23bd9ebcf36500a69fb8be017f0ebfe9872abba5070c6702047e3814dfd0e", + "0x0e2fd927efdd930e1843103c837526ea8f846230ef6ec9f0e8af61f9d3b19aff", + "0x518a527d8d9af923c12692405837e35d58a4625ca36c2041e539d9e369589192", + "0x0a697ad4d29f33cf10f6e08024a18d36a09f459f8ca427cc8b36def0fbdabc1f", + "0x5d96d5562fd252ca3b5f2bc6e6439572ec842764594463b349fc50cc8681d8d0", + "0xa093f86f7e70f80c308ce12554c6c23cdfc02dfda87579335ff1983d549619a5", + "0xef0d1f6787ea178b22a66632a6e44ff464b7de08afd8dfc780f39720b4746a08", + "0xd3a6e3eeaf50eb34d9af9ea4fc461fd4784ecbc6a2b7a8eadc37796d7f00218c", + "0x4c1dac930f116474ffb251df529847732dc575ab404342e651d6fcd67708be96", + "0xd5ef4aa7047c1ba5f18e81417886490624b5520743a1cfacbb04df4dc7724af7", + "0x2c0b01e814e880c5d9b9e01c08671e9a5bce1aa1190e58e0fcee1acf14b36cf8", + "0x464c6952f2738c8b28819299565a7ad027ffff4bd74d06ffe210ac5a44760975", + "0x5bb7a03847d5d77fa84567f913b2da4b87e4d2d453bd5194dcb15a2d07fb284a", + "0x7dd9b9c68e2df4a415c1c4898e979229a764cbda85c215bdf9b8eb2fc0af21e0", + "0x75048e56ab8cdf0cc6031e3a57fce809e540664872bfef2aa32fcedffc32e597", + "0x6c570e74626bdc74454167d24b0e755469e57930c0c5514847db10dd224beede", + "0x511151b41b26e5137b1e564295744e6b9115a277fe569a355a63cd483e2af7c3", + "0x885899509c8d7dcbbe3f9b6fc3ab2b1ec3df00909c92450dee7b329ba7abbe7d", + "0x40bc202e2040897eb3e06346d5d5c71276985470ed2e582b6f32ab8e4107984a", + "0xb442612b838cbc33bd65416baca317ae931bb2b6724bd565ca181a4aae95b590", + "0x23be2219b6842dfbe704c7fdb2bd4723cc4c12fff81ef96e0368f0feed5486ba", + "0x8ce2344dc7c2f11924f54185e191f22b536ec7a9ed82930e223e0dcac3ea6649", + "0x68b8de1cd88ae7fd5be9d8eaa51af21efe55359e5dba2aac1125232d01ddaf1a", + "0xc771dc187da76a5c761c9f00dbd9f50bbc0f149ef98f897c519457104c9f1a0c", + "0x697ef9d13cb317e94d4a40b9c6f45cba2680d6a2247e50ceff250be0f0934fa0", + "0xeae45a6899bdc251611165055e02b3fe3fe65dd938fbef43b01da4282d7fbe8d", + "0x3a352faefaf059235c46420d95a5d26618933e993ec97dee1a87cf55be10d4d8", + "0x59a105b2a42cbdec8ac080fbd09c98bd671ba47ac654c69fcffa0089ab9f4d95", + "0xb2e2c32ca966983c1951db62ef157be6824228d14468d1cb9856d53f9be6a9f7", + "0x67371fdd266043a11d59d4f05f0c5d920eb2f60b434928d7b558099623050c9c", + "0x0d0b97a7b38358d661718b2b77f50d1ab580632f030985d9e44ef3116cafa38c", + "0x2d19591fcdb7bbfe41412891fa43201363d0a5b18a48994674dfeb211701507c", + "0x7b42ac2c855a619963157ad7f3d36bcd1de0ea5689dd3e454aa2afc7b28dd071", + "0x16298885164e7c509b99d5a05549f95f57f3401f723cab1719b76a2bbce8b0d5", + "0x91b028e3ebca0b60cbc4080fb0f816d834f0898fd00ff42080b3e8dceb28d66d", + "0xe3802595c56c2981e3a4b35c149e29c421107c47ee6df2b27e979f59bb06cd3e", + "0xd285c7cdd2fa2eefce09e902c86a1856ab4cfe33e6695e94dca8fa74695a5424", + "0x2b78d2e0c0770ffc67b92d56c4b26a675e7f3639ea394842edf9019ad6920b2c", + "0x495f1461f887371cd29e86bebfedbd429f7eecbc7c759ccfa56500d7845db4df", + "0xeec765f7d794010adaf8dd7f07674eefed3f6d5b9dc43e39a3b3537288b9c9ac", + "0x1a4f6372274bdce3bc6ea83f768561d6c9ed996e72145d524211df0130f3b7b7", + "0x3955a47abdd98eebd3d38244e408aab387096dfa74443b9f607fb6fa59456e64", + "0x7930f2d3fb73869045d7b144061eb1445ea74202b8f090f4f017c27e1c4350ed", + "0xfe0e4fed81793226f51942fec125072e439e701bebbb049337a5f7b24576d29e", + "0x87a4cad6943d444f9da79e4fcbffc198c96752ae72ee3fc2487a82e724a045cf", + "0xde75d28f56b1fc3e411dbfd9a56fd748de3f7490b931328f3a5131c143832218", + "0xffed1a9fe59006557ecf383d162f0f6011b59f63bfc693d61b77232329f8d956", + "0x1cf303aae3e5d342a33d369736563133519e1434f981bbe6067f444e16d89230", + "0xf3be3a8b280dde0fd0aa8322cb7836136b8801420602e45c8ceb84ebed7c7e02", + "0x2813d9eec11134a3987955f4389d4bf07fb4b986b700d982f89d2c81caa2ac1d", + "0x28f236d43bab078d0e9662be120609f8de33bb4661596317905815e7771b42d4", + "0xd72f262c68e657dac4247a7db98fd115b1865942071d0eb06f68ddbb64b392d9", + "0x4215eb42c3bb1333a0acfc8240f1b5d9938b68acdcf0c048fa037d90cd6c59aa", + "0x5a70ad627a347a1884a9e7fdd6c87cb7c0d5922ad69054a3b9d56e96aee436c0", + "0x8ad7e96941d03426434d6b7e0eac92a04e94108d2b1fe71178b582f6d861e2c3", + "0x9902aa9f7e7f1e9e1da269bb387739c103b3ff7c67032097e309df96768b21ed", + "0x0af467ddad68dce1180ecaf84a397e5c8fc98281ac52c10d064e5972fcf87d52", + "0x4f945046721c0071c9b53a8324b391126d26c8bef20435d60a6aaa69e2d156f1", + "0x78bacb8175c8dd89e4c12b74aa687211114b47906d763e0f26cba8410e5e3300", + "0xdf20aab8a9e472d877262d3985813365acdab5dffd05139f0fb87eeae10a4627", + "0xda39d3fa1f48b617d3586f3700119f60a294c1e3d2546393e3a4bf09ba5e3030", + "0xb6ee6e1a15c36710e6d37d2cee67f4646ac94de616c3ad65b08cf6b7aa3b32c7", + "0x17fdf37e749a06f9b8768d73993bb7fcff8b6da75ff4545c1facd812c5c89031", + "0x17c88f6d416ac2e913cf054b9116f8639a566dca223243fa9dba0e0dbe047597", + "0xa04fa8fb398b38a48e134c62d9e31fabce109651e25fab8d32318bc6409edbcc", + "0xfcc5dd8bb39b3782154608c5e3c2a6b050382ded90f0b8352c645ca2b80a88eb", + "0x6b3f1cc76318f33d63b204bdd72a009a7e2acc1a81ff6530739c41990de6552d", + "0x5f4e8c17b0f337f546abdc595f953cfd056015e3add1aacc4e058b318277a3cd", + "0xd9917cc1fe0d4b12b85b2713c9942cdb07178e4c6f594baa4fc2617491d0ff07", + "0x2f6df9e5f1b821365dae46222c7f252a13693fc34a9ff96c1038f154496b49a1", + "0x5807dc551b31c69db61bd1619ad0f9631483957bafc20d4ab1a3e6902332fca5", + "0x04b97d025d8a5b1ac619c36ba6dc8185c05b4f1244993f684299f7368ebeae3f", + "0x16c71e2b84a27105527ae46ad63c9e2eb8d2145168c285ae2f7065e5354d768e", + "0xa562d2718f87e2086106f87bbb574bcb1e767cbf9471999018eb3cc21515b004", + "0xc2ff97b7e8736ef6de59a226cf0ce16353eab76b4a8b8ee147995238dff57d73", + "0xa9abdc4a8f70bc74134eed2e64045b24a1e9290ddbdc6d833179fdb4122c2293", + "0xbf0473bfd49593b516300cae47d7e603f9a2ac0767db39e2ee6e028083099490", + "0x8479dc0704190c2e34795dcb6dd447a4aa25672b9e45dd2e39f3864fc9521203", + "0xe03fe87b29737c7e663196fb33c69f02a35caed2e791f2a15a562bd2989d8cd5", + "0xf50fecb879d837e8556ebf2a5ae833649ff8f97118caa2e3b617b59153b65b4b", + "0x0eddf32c7032a26f6a0e0cce24ad9cb266d11500db33b863dc5d3470e3ee0a60", + "0x85161c9ae941dac62edc22409804ff1ae3111d9f903843a7f86b7a138e702da8", + "0xaba0acb42e5acf464de70e965b47872964d7241a57e21fe753535540b8e04a53", + "0x70de7226fe37eec9dfd4c7902761d0ee25a24748d47dd73202f9c10269bcfd18", + "0xe5f9e07e4757f99c0b3042102abbcc59b1472f45b53313626fa49fffb13a318b", + "0xe4e4be40e0d945d84851d54c2a317f0c56cb1a2a78b9efe1c58d996f9f7be4d8", + "0x5f45e651327d2fed7f559b97e56f2c954e82b843c59cd2e550628639f7f36d26", + "0xdb33bd941740ca4d547b52f857ab2fb06a5d144eef147b5dca309b6edfa730cb", + "0xdbedd24c3f6fb0f3d1fb2eefeb658076338a5274f01bf988dc65c7062f211d73", + "0xbe54c3757785c7346e4e94e2f975e97c6a9c542b2a621281b95529b58d3ba7cb", + "0xff9a08a7d3bad55b530fc2ce5b8437ad524b176f151b10366376e8752630a1fc", + "0xb8e1cc41b404d9b605c67ca266bb338f4b94db9d04f71c33b6205a7aad5e581f", + "0xbc06897480daec866c9faf1723f19873448feedff8237f8163ccd796dcc2a4c2", + "0x1681fa8a577b819397aef568125a2f81aeb94d313c5f37c8041a6fc8cd4619ae", + "0x91b95548f18cd07f37ae2b7d53c56a37dba797361f986d1dbf290e169a8f9bcd", + "0x2e0daea275060cf0d7759fea5f50a5d22bc13c2ad85b1776154409f4d4eb30c4", + "0x1999fb0544b4155419e2e84fed331c401f6a2e3314117acc21da81a135bdbb47", + "0xa35676895a52c8b1de46ee4059d57d1fb5efd480828fdb120048294347c3c18f", + "0x8892717f9562e3f1e5dd2f88b45c299ad91e8ab05dcf6be1c0de7a95d0ee7a3b", + "0x91ac521bd8ddbc2efc821a82c6336d01cca79b55c36fc05932ba588d68bb62ef", + "0x76850eae9c3e3984a06d664df14c1e3ab86532b16624e93603fa098df1136ec9", + "0x4379c37011e615085ad33e48b21781b943437e073964033d6bf2dc9fe85fad21", + "0x15cf24d1b72cf5c797531314204207e899820c07f2c611377fa4a8d2b23e9e64", + "0x964b36d4050d4eacf96a2e71bdde62d6908efc0291795432b45b3032f78c5fca", + "0x5e8abbf414ebf24bca3f1db28a81e34ab73491ef8535fd661661ff7064b989c9", + "0xc1499796680c37d46d0844a902f0ef966efc8117c2460e73f89b1d81a3b35f76", + "0x16be9d9569bc82d5245e0d2f5f2efe4a52ef09277d84efdfecf219ce5c7368e4", + "0x05c7f5f0b28e1fca9441830a38e54813e4ea3a73c7c4f7a873781040e4718c95", + "0x0902c45ce41d4e6cbddd66046e9baac2ff2b6fe4b4b5fce0ee91932d57f74edc", + "0x523cb2017561cba6cabdbcbb54ba2f97d6a52a5ada114c533912c457a4fcd02e", + "0x9df4d35c4f05636670123ee3d9a6255c9adddfd7d17cac6ea2b6e8dd33becc09", + "0x05638d9e998b4eff7249981556c9493fbcdc39f78d34f6c120cf87a0b4153316", + "0x451c25c490adda5cecad4a7569450e4be71b08e6541f0e122683ea3544b74375", + "0xa82e2fc3c1d30e7bc9f5a4a83ac3efdd57a7d05617889001c075086dfdb0d206", + "0xb2e0beed5b15c9db6e0d4f31a2111c4dcc193c0dae3ad9fd320d0e2bd4410ca1", + "0x40183f421d938e028836b39156862b8af8f88b207ac17b9f7535df0f6e7c8bcf", + "0x311e6dfe62efa8e3b5ee21ed0abb73d1ec69b357e689f22d04d7010b3e6c9ed0", + "0x7abd56b8ecedeb87fc6ae95d2a108829f8ccb109e05d75ae19ea67bfa73a13e1", + "0x46771d8f8adaea3d100fa756a801c22d277e1ada2256fb7e613730484a856de3", + "0x3463faf96b0c752199950ccdc1ba7b16c07df750fb76c10c8ecb1b155e3ce021", + "0xddd0b47ff5d1fd28ea7bef1247f683c3c2ae6356b8260376b390b0e4770c2c70", + "0xe50cc4bf0b3278ec508e7d9d0f233e942721dc9116c56d65182c5afc04219574", + "0xa567e9287f6e919a716ca159f6f59e0ebc6e27bada9e0312e4e7a248f3a1a195", + "0xfdec56f24cd59d833907cb21a4311c47d4baaea891efdfd4cc1fe48f875ba73d", + "0xda2ecee4f7f28fd5893d7db76b248fd5a5c8ad18002230177438f1209e92449b", + "0x267e150fd803d5dfa94dbd099f8960c52278fe338d1786b1678ba7de6c101625", + "0xf70ae6d7f25e486d08489d70481e7e537eedd8303b71c656e80ec04359dba398", + "0xa02efe6e14630c760bd69af9a6fb3f8391b5e7ba3cfb8bad4958421b3cd067f5", + "0xa5c4ac4a8cf93b8695d2f1b080ea00745b52a656b6f55e4debfed402226f3605", + "0x434c8b7d449c62e779aca1daba7e2c5b7f264dca8879768dc0b9c02c02ff77c1", + "0x67268d79875e46e0abd507e67b75e5cbbf9aa3de463284e96c057ce2f3be893d", + "0xc3d6c58cd4418eea6fa0e0be7de002279c761588135be2dd2dc152c6f580d75d", + "0x52f6b52e67e84c9cf8d56fd1dfa7d8e45e4e4129d33f9a942307929d28ff2859", + "0x8833fecfb8236f00abb49621694b0fd2a59a41f41035d2704a81d05a196fc4ae", + "0xca068fd3b005b08c2f3daef99d7447332a7720880ad94aaec632d54c591063f9", + "0xa3de1f4bb4c142ad685773b15e9a4e97c35267eac119e3c29e0aa36f2b814487", + "0xe8433c0f128f18299bda56175b279a5e271a8af30e3176aa48f6a734352e5b1f", + "0x9727cfeb52499b591e360f88de264235661a21fbe7242ace5d849d90bc506c74", + "0xa0c2265b8ab7baccbf56801e5d06baf4ce6af5879bd7e193f5fbc67a717cf2b4", + "0xb6303d8b21a2d08fb82154142bbd014329841a5564adbd217468597302d4cbb2", + "0xc6571041af75a5bbb7552811ded08fd4d93b8e33e974451771a629593df7d656", + "0xcdbd84961c2639aa0159de12abaef96152026ba3710385ca44e2080b5ef6008b", + "0xec8fab8e1adf154822c799dd35e2627420663b4d1f9439c79d89f360467fc216", + "0x8aaafbfb4414ab5c8ea23ccc43b5abd2349194cdf98792bbff46b7734a886d00", + "0x1e2462355a74d1a705e3c3f28d826a6b229a134c472f8d2541e9d03ac37d8d2f", + "0xd4e33157363845de8fa0564d1145e4a46eb22fc909c6b182e6bd7aba4b380613", + "0x7949178782807772e6f9160a14b54fb0ed503d5c1630932cf17cd9e385b00ee7", + "0x089d7d1b5a0f3ec62db8d1c11a95365688a47bdb14954b72770d5cdb7af3a365", + "0x69fe40a2c3b725eb25be1310ba0b97e8974b17ce9eb233eafc1306a6ccd7a463", + "0xfb4cf8727985a7d97917d3b020ee11dcdb08cded73085363f625a37ed0d8b04f", + "0x5ffa2a923b9a50832644a8bd39f0ee569f9e4ad8f50c4e1d57c9ec96a1c03477", + "0x8360233860f889d96a7e077d221b4ab6d9e3f7f1c5fcb20287ce486e9c244979", + "0xeef9332e4fdb2022128df42a2f311dc5ac080c7273e1a24b50774058b1d9adf7", + "0x89d790c825dd9b3de1635ba3162fc0a3a99467747869b409db97d95f7ff6cf59", + "0x04a088709bb769fcb5b670fa79cd97ec2dee21e62992cb2df693e1f18b3a2d11", + "0x86519becccecba9b91d0af72c176a414589b056dab914f877fe8032ca1b8ef9b", + "0xe61b3d8ddc856aa2ab755d4097ab9040ddf3554a1d8493fc5b93b5fd58aa0fa4", + "0xd8efd5557426e233870b8bcb53e0fc89fe79545d4c2d1aaf13409a7fba84acef", + "0x0b74a2eefcd26ca8322495468afa0c2ed24db0e4cb8ddf30bb84f9d2bd9dc4a4", + "0xa9fbda8887f5ab139d567d9a14c507e574352003b6fed49d3cc3ea49ccb544c0", + "0x078261958231fa17c1b904ec7b80ec7c2a0e49925a2309abc701521b90306b6b", + "0xc4d9c5af76a0b6c55ed8243985779bc77d4ef86adfa63a8ceac44efe908ed012", + "0xb02867e224a85e4a929894919fdd1f3460f909ac276278129d7fbc859f56915d", + "0x7a66267c51322887433994e3a01fdff4600e596a06e1c627c5a50c42e2bf203e", + "0xa66849c9503997ed600daabeb23c1e316d938d7c36561013f41d9a994d819735", + "0x572641c0124606eab803d11c3548ea828cf67b3d238eda5efde6098a210ba6fd", + "0x0a920057716b8b6b968bd5a28478ff99224b687bc6c97e26669fa1dbaf99850e", + "0xe198e1c691d14b3ad0a6b1888f7ae40ca60042ac91ffca0d6df3b4bd12090898", + "0xcf8cceabc0ccdcb4951cc37f625c1e8c120620e42ff3f1ff4a72e4f6dd6139cd", + "0xfb06a9c8215be6da3ade6c298f565f7c3116d12c87da1fc9fa48d17240e3b545", + "0xc967f1f8b116fb2faac0ae0760622a488cecc6498c074ee0f013d985475c914d", + "0x7351f762b23b48612b1369c3a7754ac2414e4d337f94cbee4fc1adc29759eba4", + "0xc3410fbbcda7e7bb868ed399d8950db593b7e79d5fd482a5fdb6e6bf342fe6e8", + "0x68c7626ebe19b8d30af71d3cf3635dd7c7912aed7a47bec3c369b72eedcffd05", + "0x3de93c3bb133dcfdf07ede31fb2933d8acda317e7680e7bd18cc1485f30b9225", + "0x4f2c0cc1f9f2f2c1b84448cd9b033d95f74940f6dde28dff0cffeb2840b6eb67", + "0x4e2934f4981c6a3591ba4048b0f3794a6b92bbad76e94d68ec4a99ff95601d06", + "0x8ae0d36b4e3212e9055cef96398fa706d9df90c2ced2c89c4828758b62154840", + "0xde41a196177d404534cc4b46f6b877ca4dc22a443f6a4680915e43046396e8ef", + "0xcd273ae452ad455f430d5f57d6411454fd2fef1698f9b8c23a4fec298936e47f", + "0x95c9ab467c02ae75c80cf924e81ff6c9f9f9176b828bc3dead827d4707fc5cdb", + "0xf9ad1ac9623d7b8b55dc1b363ac5e6b81fee9cb0bba042ee9802b74f3d6d12ef", + "0xb77f49f9fe7da05e6233e1aafd35cfa8042f8ac8220bb04fba898bf75b485237", + "0x6c7dd1ae1b2c378203b2be05abaff1e6f24d9a16d2fc4fc76bf237e61f5b0951", + "0x50c1046183493f9a0b860effdce20c193a7c7e89cbb509cdbfc839c522c99677", + "0x38a84d66ab731cdc96ae16a7f0dfb8c61c586bd0755cd409f4a785d426ab2e74", + "0xedf3c6c8ea853068d511c74639236e1daafb2caf96964e5b0daa22d354e39658", + "0x3445d411938c39b0168b96f8a4b0e32206a531910bbf52743faf37b3581f3236", + "0x4c418ec9d20b934a3f237b4c13682cc1638ba8e517e86d168e8c990971098f97", + "0xa610b6a8777a0467100a3581087f5bbcc1e7d4b697d9f53d00d9fa6f0d77247c", + "0xdc9e46a16000ec340bbaf70284f6a1528dbec79e4e8c371d4b04847f8c844089", + "0x34e76ef7897de33eb66e4ae37b5534c6240540048c525f858dc832b166593181", + "0x36dda3dbe43f18880aad5a4f25ec29bfa2da4a6c7f964e6418c0cd421bdbea36", + "0x64221de18086e9361ec07e4cede9e0103109d315d97684f26f05e831ee2878fa", + "0xd0fdddc2384a132f50ae0670cac8e62e918aff264277d350faa66a0e95430a2c", + "0x4da406b43c8247743de53b0a395e47204ca3f0d15f2a29d8715e11a71001257d", + "0xd0e67a0d70cd2140f55941d6ca90cd15ca6886ae013ccca40c0afc0bb8bbe016", + "0x70e6b1561209e95921f3800b1c48ee3bf3e532f5571bb2e4a5d82e9b288eaa33", + "0xbbe85ae238d623b1434dc75fb5e5d09a75578d2b85c59b8e69d635befc784248", + "0xc617186c1cc1c807b9626ae1a68d8d4fcd6b42aa153dc586d5ee2192cf9918ef", + "0x018329ceb98202471fcd740a34c7c987c2daa081b0d105b86b5b948c8248ab63", + "0xa1bd431642f0e1dd5f0bf01e610c29e5d695b8f578faeb0f511a7a9a2fa3116e", + "0xc8df41c2aae79331b86e7b007a28e2731f27f50609e9b4924678b8ff24d509e4", + "0x377c56c1c8b9686225cfed2710e4934c17ce890222c7482e7fe7fc77ebccc52e", + "0x0262617467f33b9eb0f3b28f368f5d08483a38dc9423510442eb88537c6d49f6", + "0xcdfd57a4737a718352b19c48aa72c41b2be0a7267ec7abbc3b539c81fb5a4b7a", + "0xace32a66c3c9540cbbd81c26a429487e3ee135677b8a43ee7468c0fc0c584c05", + "0xd4a50046105d5d01c4c928a0ade2d1a82db88ec52dc1f02b7c2daecbe9985841", + "0x350c5fd3734cd35dc6e02080e087858bef8d8dfdee44bd8e2bf75aef795a961f", + "0x5e597461c6e1468b3056a2cd4483e85a8b38dc27195ba60d50ef9952aa640a49", + "0x4f9d19414803bee7dc37e16413e033bb710543d86b38d6133adb1ab4ce90cd0e", + "0x3470068fedcdb6183c16ab34a1743e2414151eeb7a46d71961c1359adff1e214", + "0x66054a35bf4b53cec8302e2c94b8be80f3692fcbee87da4f894769799453771e", + "0x752d55b28bc47a388b098fc5b6b5f60ffe153459ddc1c0da007cafb3be151be8", + "0x3b969e58d42a927a7b214ed99c34ca6a77234703908bbf4ffa17e17e21fb8cc1", + "0x6f0157bcb211940600bb88d08f026fe4af881dd11d8dd1351ca014c88c3db72c", + "0x8592dac47e72c7bc32099c102a582c00543a0d35f2dfbdb9b57068c79d077815", + "0x8c95e048954d689645df63ae447101dceed5e75d154502c61f37990e5a97ddca", + "0x9f4a5e8e60a88088c391040bc01c46538f0cfc032344a882bf5f00cfe1b10d3d", + "0x04817ac392930b9b4d81cd529703b6688717da8101fa8a327fe49c503c0694ea", + "0x446d3e1470d3f965fce24932f5f1824f998c9cbcd9eac1c9ed7fc7a323658bdd", + "0x8603a9e9b6b852768b751175cdd2b23e354777e38408903b42d67a108c1b0999", + "0xd04177d8e00ab233b6336da228ac1b9d683128c66a85f1eaf2d462e7d52c9ac9", + "0xb01c3a41c7864d5f14f7b2f18bd5e4576c2d4d296d78927f75dcc50f488b54ee", + "0x6c7f1c0024685c819230e8accfc6a0e5f967b440d069bfff8c79185f152f940f", + "0xa2106a57c2faaea7c6ed8c7edd48e64ffadca5c03674672da9a972b4d32ffc7c", + "0x2c245fd6206db27c4f95be2fc01b04d24febc0e7781efb69623bc5214e7ddb69", + "0xf4aadfddb0d2d00668dbb5a7aa1cbd4302d0778d3697af4cb2ceed68e5dd1639", + "0xca6ff39151d73d4ff9aafb89ec0843e5d93eceacdd69b1c95cfc745857397c15", + "0xa18081aa619e1ee71c310a810c8b3198d515a0954edd34994fedc974f24bd0a3", + "0xc05f8f372ddc52ad91874e45aa48683c969331a3c472708714c19af1d8e3dc07", + "0x57a2027a86cf6e524d7a4e7b0b920a98ddbcce80f018bf402f7fbe73cb366614", + "0x3fcbb5d3f51987fb786b0ff83f39526e2aaf1791abc5aabf2f11a6ec8ea4bad4", + "0x61dbfda0b2940f034dd239375ecb5c8fa7385547a6ffd4bc5951bca8f5c8bf0e", + "0x662820a806693485407041ced34740b6ed57649bdb2d346aba5cb77108f697bf", + "0xe368491b3e2fd70d2c6f06c6e05a41f2bce9fca28df109882fd1bf3e47c82287", + "0x6cedbe3468d4903c73316b43534912e1b69cba4623791121b309fe37b6ad9b64", + "0x6517d421dbdf6e9041df293e6112572b8229208430277c85a7e52acad8a760fb", + "0x8a1602ba31438ed89e0f922aa165e0740e9cd9caccd82ca7ac79c176fdd0a2e3", + "0x09a07f8791bc59a20a326a727803822d1dd8de5e9281870818469145ee90f114", + "0xf6c3a82062633e2072f5b2c59b78b3f6613663afb2b35546c1240c696aaff8a5", + "0x1b4ce49dc9f835afcb3c34e92caa9a30db92c0fc2cec32c1746cc5eda1caeff0", + "0x2bea42fdae58791ac76d7bc2fbfc08712cb8ecb70b011a271572f047a678293b", + "0x01964b0ad4072d281dd451f41ce9e2d61f990bb104fecf29baf012779652f8e3", + "0x773cf0d41e03244bc688be7660101ba39fbdfa2cc94e6bfb02a1dd291c5c7265", + "0x651f0346eac119d14bba1a1078536dd986024c4dc5cc5953b4d9cad09735f97f", + "0xa4389eb16c42465582dbcbb6af80198f8412091c6ef2ecc7ec84c093f8925306", + "0xed75036dbba0069f415af01cdc723c0901676182960e2e2b51994f0e0c6ce0e2", + "0xac433ea4fa502fe9d126115a386eabeb86df2c895e3908bcd02a9d7d125dcb00", + "0xa65d04561f73a2f6e1f107c3759143c136b12619ab498ff7ad31994e7db5438b", + "0x6d9815c2f081d54013ad118b2c0ff456aa1396eacae1dffef461c2f2ba066527", + "0xab192c650d80aeba5b885a67d07512a6f532fae12ea69a3eb39af7da95c64b8f", + "0xa1d329666928263f1980f72c9a21414c7302fb5e77cd1dd7a91c4dc5d6d748ef", + "0x9e84e47ddab8900337e35ae9287dbd041d0b1d059582f95d375f3231eefd428f", + "0x89c11687a6c5e5a5b45255b4a8ffbe337d8706470c3a39063a960d103ade6af6", + "0x3547ba85cf24a64983968f8587f28bace1a797a93ecc16ce6787263f0de18185", + "0xa98634b1d1c39ec41c7cfa6a4f353c4c276f0618fbb3bd257008b43bb33b262b", + "0xec39ca2cdf7a3798a1530c8bd1424622822f6824415b2964d0467ba313faa940", + "0x8e66c39bc71e16085bba945ac0aed19f3f169d9d2dc7c0ec090583bb896e1a51", + "0xfd7f6e227a8c9025a57c73ff8c82265bc69c9aaa185eebfa924ef608234f1937", + "0xf55d1247afbfc35128a41465800daf2f90c1d57866e4bd37f92bc8278baa95a7", + "0xe82faf145ba98fb5af7e32f5aa90e7b2aafe9e2074c86bcb2aabe17ff35a025b", + "0x62abf9c26e0e5a7c2ca3047c8ae9f27c5215846595b4f41aff72c8a05ce42549", + "0xfc5cd59f7ff8b54d787cc77e29af8f9f78102be33ab4fd561481dbae04426b3d", + "0x69af5e5777a5ee39cc76a049e268845af3e1ecfd0b8de8644197469460880a31", + "0x8c1ea859c343697564a88b170973903e588967d89b63d511f63218bafa4c4aa5", + "0x8f9c38834fee0d5653d4df327232737d342f9b389df61f5cd377164e38a91e6a", + "0xbdaff64768fcdb9210cf8e33fe94a6cb38ba068c2d21d970efe6cf5cfb919eb1", + "0xf64aac8df844e56afa1413e9765a955b6879af44202a43e3f9aa15db421f6643", + "0x06762bddba350befcfddef5148ea1f05729760cafe5f018eb71285b92e30ad96", + "0x536ff0949c7f34762613747c2b7d14f605d6bf0ac328346e34b4fae516771af3", + "0xc30d536522242b0b805546882d5a283d517efc518abbdd674156b6f67dbaecab", + "0x818fcbac4a40b856bed993b9021286fa5f59f0b76b25af731f4123a83b5eb90b", + "0x2e6c3d4de85428210eda505421b253bf3964f5b2403084a1202d940de2d6dfdf", + "0xb31f7199cc69689b24ee2f52c8b50746ba3ecba904c49e2b352f8d6a64f514f5", + "0xf910446802e79809cb62cf6d958d570186ec7a477a16ebf6c0eb45c245c514ae", + "0x36e60616ab618daa724963f56bec00da5cbfac517a41f291da6e6cda890d68ff", + "0x4ae4e0c39ed59901fc74ef1fe52643f1f4341457aab052be71382d9945115b47", + "0xf5038c706b2cdcb2337a88ff34ed3e8e3093fe95d48e0342fb3bc3c0adc2ce51", + "0x021c48a8ce17f06cf41f89033a70b201b73d2fdc5c314bc313e8cff57cda9fd5", + "0x363c94da2187a9e497c77688f70baec16e64c4454db1f0dab37925fbfb9139c8", + "0x2d7c8407dd4ac5ea3212b18e103f10626d8bbc0cd18fe261409043399f026b99", + "0x05c3dea4c6599a259c3297c51c38c13f618ee5372c6b43e1945396bdd7a91229", + "0x7eb49d1da78b683659a33b9346c65bddcf6154043c97438274394e3ba868d0ea", + "0x17c008f4a8fdde5d0dce03adc57310580912599fca0535a42144526b4a630503", + "0x0011edd3b9175a765f1ab25a3123b91ec9ff9f233e8f782a2001ced391da2f05", + "0xcb62dd00e7a67019c782abd7e4bf7e3eef12614b03f90219434bba24e3070097", + "0xee086e5721066d4d8886e61e228facef0f518f8140926067be9f0f55b261558a", + "0x683c738a40a8fb871f3f7ef575a422d9fc60511ef4c84cf0fa9161605c10ef2f", + "0x43a4ef42030692e416d28c72f899fa83b4e3d3066fedc72d8c1ce0c3f7f60dcd", + "0x964a579e0415c2875382d1dc5953a54850494ace6077f7ed247f00eeada0bf1d", + "0x286bca4d5a0ce6fe9c9ecab2110fef1c13f71f8e11c37ab5667e164d2f4cb3e5", + "0xf2faf12c85d82d434a5db7d43d51bc99508bb11db731d7a22136d08f57901639", + "0x645944ae04d68cf3c993f75d75f217c8a78531ea3f17718aea08c849bb705382", + "0x7e4c87b6078ae798210fc39cb080772ad2034dcd9ec0f7a28b2de7d7e15377b0", + "0x4760f5e4af303ec759aea4fc2add0759caa3ae96bff4f727c0654f81d02a763f", + "0xe2f15372e72f6e8852c7289b192e959fb797fed0fb9581afd755e4352ec288e2", + "0x760975db833255fb7f325477153462be406b63ce08e6ed0da0dc48f9dac5375b", + "0x2ea20f22253680c49663ee8df8c74aa2f0703223c5d0dcb3db37b8c3ae7475a7", + "0xe7622392c52ce0d6cd698806142e6e26c9d86c81f1877bfbe9022ef150cae57b", + "0xe973760c42ebed38e37252019fd73f4ef14f2503bd3ffb2d859124b7e25ee557", + "0x7d0c3cb847b2cb7c1a5341e0214e830ef886073983e08498f89617b5833c22e0", + "0x5f5939c73fb684dd39227cdc88af51936e59a9881f8fb63e071366b9df4a6b36", + "0x081d5b53b84e2fe7f8cb04a6a6a045e4c11dc726710b6e9994f426fa9d32ed9b", + "0x436e895e16cc13777cd0b740538a64bb3a1360a7038e6fe8efa3ed3f590d05a3", + "0x55a611a3cc6106a86768053e268658cb541c8ca17a66a4a90a375772f0b5e016", + "0x583da6ef703398a2b781acd935f9916932050fa0cbbe1548b86d40b3b1941fd4", + "0x055531e88f18a0d79546a3bf00cce987c33ee0f97ecb7550b3c301530b279bec", + "0x4408c0007dd2dabe7814546fdc336f3ebc5a1dcc7daa1d8a38a9db819eb86374", + "0xcf6e7dab9c72babdc051fcf35ea89e793aa6fe380830d61870cfb4ebc3e7d919", + "0xdb02f15887419e3f89e1930d620748ca53e7f0c026ea8b60cd6e9ebc62f9da1a", + "0x4d1bb9da4321c7f2962a275272c6c1b1c3da6ca9549c19134dfbd43cccd76359", + "0x6ea8fc2a9138c404993edc83b3f54b0d5d94c85367c8e6884ef5d889ccf1bfa3", + "0x4ff5c77a56a90c20872cc7ab5b868557f7fd5d0103503d4217ea2d0079690ef9", + "0x5edd2bd35b9d24b9d5a3c68025061137c82e04261291c4d958d9e0e9a2763329", + "0xefc287bb7ef397298052013ab79d5a46a3a80a55f60e42c66746c9a25ef2dd15", + "0x986fbdb20fba887f3e4441a5b4b98824e1ab4df9aa43cc1d151eeacc55bbf4b3", + "0xbc70117b2b1204e3868a610bf3d2819095185245be082c96ffd3e0c2e406be51", + "0xaaacb52b835b235c76b02960517159cd1f2fb08681f13c8352a04820a990fbce", + "0x43e8b0fb11a657e4afc022573de30184947153497936d6cd47dfb2ccb61bb110", + "0x63fc1cbad428e16b1e2063db10c85ceb6684680c044adc2aed184054492cb19e", + "0x776674ec78cddc642e69ee9b16f2ee876655ca3efaf6a24a72c99a998d55348d", + "0x09dfc550074184d46ec04de265047ce529bdf2c02f7b44e428742f674c31a0de", + "0x897263eb076edb3071a306922207d17cceba6ad179d0f7fead2912ee42c69774", + "0xbbd230729752cb6d078eb853681e85823d2946095ba318d44b28eca479bf217f", + "0xe731c6c7938b7f2dafa8b5935cff095289b993d5fa4dcbaa715e6817d83fc823", + "0x04fe3b2d9727b05033eb4955f0c6693b0e63de665db904bc62b4dad5ce59a487", + "0x881aec4ca776991226a24f1ba9c736f9e86a051cffd1ed42d563c01a602fd126", + "0x9dd5adf0aba333d3cf13bbd8636dcec128911b675f159d11fadbf75137e1b766", + "0x26fb6742fa3be87da75218a1a138a3ed64cfa939791cbc718c181e226b5ecebb", + "0xad0c9794ce6109aa72c3b0ea6110fe96191ce3efc89bef5a99696ae84380ef3c", + "0xd710b6759276fc3149afc75ede514256c092a9e044555ece9fe27e27364181e8", + "0xeaf3b11fee973c4aae9fd19de92a7b14ecd347275c64b2343a2e4f97d1204785", + "0xcfc68b631ffa430c95ea583a1a1cef0a43e3ca182deb646c399a451ce55aa921", + "0x52057b9108fdf67ce8b7a28596e933ff57369b9ec9f1183b79cb0d0c9f32d651", + "0x1b99aa1987d512c71cfecbe56d6bdb4828ff254d055dfe1b23c472ffa4a6c3ba", + "0x64968b18cf678d322a84d54b2a91e77f44c108e2d722cb1f52708cfb824d227e", + "0x51be4bb756c2a3b74d4552310d49c7edf084014ea2787e349bc32143295380c3", + "0xf98742fa2e646e8add000e8ff892eae0fbee6f61a2a200eb129758285938a881", + "0x0f963737d026fc353f0576ae146a0dc7a8a27f31138b3753101a07f0e833f1f3", + "0xc3af9d91a1e44a3e865f44e75cffd5e9b135b04fe6d423895fb1911ec08573b3", + "0x366072ca53e1b7c312088a2c3504894c2bbbe1f1fc3204e22a22b5b619e09a9b", + "0x46ca1ff3099150438131eb934083404728921e498773b5d91ea59814e8078588", + "0xe2b18d6bba927615a8f9252188c141864cd61cfe570ba65f7290e4ac0739b403", + "0x6844fe922e79f335ff103ec551476f4433d4617bf9f3e25efe5340cff0c9406e", + "0xd433ef7f7b8d1d517815e5f77f95a2f3e5100a08a1ba9e5de78fa57076d1d307", + "0x6a2a72db31e9cad268f13dd07c3c91d2c2c23e0c92c6f1047a6f3d59f447b821", + "0x9f0ceb8da593cf1c70f4eeb08a3f7dfb6f682ac4bc1c175efac43b10e100ac46", + "0x5ac594092618aeb04d6ab2c5b2612f1d4ad57162e62aff2d04decb0ace6658ae", + "0x003792b77b6c078c98e3ebdc29014a2c24c238a79e71ed5e36e27c1e273583af", + "0x15e869175252c89249a7728c33bb8e4c67f90f3c802d80356d25344ea05e9249", + "0xfa67b16cab4a3f1f8c4e8f6b3fe98f5be59e0f13ebc50d32f9d1dcf55fa4c984", + "0x3259027d8095d69e4bdaa0ed9c7d80681f4e40222b1d03347a141a634de40344", + "0x8aa2f2b46d0f85d66ebe39c00fd999b67d576e2fa119fad6783d6af4939fb25b", + "0x0f6073a427daebb861b9e1c45ab46ca9b8410e8f094137b979f6d17662fe4ed1", + "0xc9a4e1be21bb3dbde859c1bbd9ef9b63f8312f95c3d961414338ee8f9c8611d4", + "0x1c24d8de991c7daffe35d65f90c9eaeda59163f481140928c737d1066a95c01a", + "0x94f41fc0f4257b24ed6ee005e62486dff71f6ce8176b9fddede2eaff25d9fe55", + "0x9fda793acc69eb63981e28716bd20e8fa3ac091595929d943c651f73f6559593", + "0xf7eb671c5e563de2698cf69b2976e25a91c2c462eb7583d754211b252b4f8479", + "0xaac71c1761b78cb396167f177572144e68e1051ca3c332e94514041b9c7833e9", + "0xaec9323ccb0314b0407a591c05a0eb83e48052eda78fb5e9116cf32c902a79da", + "0x73a2019eee50ec26354b6f7d47416bc10982ac44ef47ce4720b6e73aed28f869", + "0xa45d192de4836829a913a20b14a9d8c95dadc04edaa07edd2599f2a75f85f5f4", + "0xcd8f6adf06fc2f226bcb713fcde720e205e4bae7a188d5135e3728d752835dea", + "0x59614e66b31bcaee5066f04bffdf318879126e7bbebdefe39b921fdfa5c1ba67", + "0xb27c406a25b24d78aa543a26b8321e43aea21523c7805215ded3d72c9043e0c0", + "0x1485ccd46c327cdae534d95c4b61611213377dbde0728f2ab309a65b69012deb", + "0xfd69566c9456b5af6a515aa031b326011cae67e88708557cefc5f1e67d76cd49", + "0xc7e49938469e6a6d27fdde544dcb48ef3f761d60ba4671163f505de1ca644228", + "0x96cdf2b037a658f75433883813f9edae930769dde761fde991e141fe383d82e0", + "0xc0ba35adef71f25b99450a0bb0a46b450dddc56ecea4c831a53dbea55463c594", + "0x97de7293e71b1779b23c3b51fd85e46d7ac86c55c18385d9c54d3da1c458a387", + "0xa72bf43cb3dc7ae59052542fa2bb2d5b921f49ac14485c1e9bfd1286af59abc3", + "0xbe7d7862602bb9911073bd4a8ec5421901a3a66698ed478102f02df87727fc12", + "0x7d17171e3edbeeb85326d785c2a68d6666526d8912c6aac44bf16fd0c0ef9834", + "0x3f3a36b17fbcc5ad93d99e71ac0bb2acb10b24fbb914773a0991c52287c201ce", + "0x0ddcef7f14d933eb8b28e5877e71595116a98e408b416f01de80be49a8e52957", + "0xccd5bc27319844f5b389b3b15e564ceecb55a91d748f145709025913fbfdaf91", + "0xaf27df2c5e88c00e482e78e44de581fd17edc2ae91e3b7d1017badf00aa8bc51", + "0xa850950e04076c8653eda7cd11761d6a8e9b46a070ece44dcbf5b8e99d9fb0c3", + "0x6c934e50d34130b0ea5d5776ab74fbf4ecf48f8a178115f7e93670599b406653", + "0xc6d3df6d3c0ea6c8573b314e7e0a8065d76d27333c0d6032b97637fc7013aa5e", + "0x4f7cfed4bad1d65ddbb1e28c8d9814450af8d573c0f1880d5647eb008a13a44f", + "0x0a3162cbd7a81248f449393fbd62f65ca948e9134cfc722554f28b2ef7ba4f89", + "0x6cb294c5c312be01110ff0566dd76d4b36e86c76587084faf6ff7f28b717873a", + "0x799574f8dee1769273651e19b56c17d3aa27d3422feee1d18080608b4882e2de", + "0xec0537da4c25bb664270f90fa72c0ede19127f7bfce5724762700715986f5b2e", + "0xb955c2ff50d42cce782c991be3eef70de85a1a382aaa6f7775c456d87a987592", + "0x79f931169917ff38a71b45d85a5f8e068bf45e8ece7a8e5530a0b4eb01efef9a", + "0x8d20869224be1328197ed93a88fd53d0b31ebeff49870bf7851d6e8dab77f730", + "0xe6323bef31280b55572d866f315b97f5be96148a3d640284110a23115a59ae27", + "0x47c847e3a20412135b22a2bed001bd32dcae67a70e5681a5e14a210553eff25d", + "0xb71b250754e11a7c9fe804a33e15a662321d9449fc63b1038a17d58273fab04a", + "0x89128e0b4180512ba5c75216b3030192d9b1574df87f1b926c5c7aecec92a7d3", + "0xbad078b9e9b0328b48dea551a962e437b51902c056c5b0776482712f9db786bf", + "0x4d5aed467fd88e4e026bf006aca0a78717d6b52f311d32449b1104f415bb5515", + "0xe72015b43036bfb62431d15335e719c549c61162f4879c0eaa81421017922d15", + "0xf70009d15065bd1570ef3fe2273da35e894206361590d38a4bd414ede7d69d06", + "0x2a4a2d96e00f06616d77b29a72d7c494cb6c29f50811ea616452ac769910005b", + "0x21f9218ff5737a4e6e6a167f83691dd6445a23732cdf4491ba7e24747fa0391b", + "0x26461e3689a21987bb38091f0fdc4f371002f4a84eb3b618e8c7ffdd6b3f1a31", + "0xd683d8a1f060a5e9cd282d641dd852ae20b7b1c0f9ba785d5ea15b234ace728e", + "0x6b4c803a6643293b9160d82212e5b3405b69a4115dde4de0258a24832ac658bc", + "0xe9a00afe1af35d7811d82e2312d02c5daace99f050e42388e76e5c1b745d8779", + "0xe756e9edf9bb83f450ba9791a245adcb89ee4fe56770d1894086ed6be3d089aa", + "0x729d6bfa3c1d41089d53e08164418779c9abf94d8276a8a8e7f1e4ff492183bd", + "0xd582a6b03425d0ab29d1405232e088822467053d470a99c3127c46ecc02dc93f", + "0x69ac67b95b725e5102ac7ccb59b46e4a4457e368b90f80624d84a37104ab166e", + "0x8de7f79cb5eb8dc096e9307968dd1b46acf940994dc1fc27156c5168e1ae5364", + "0xaf5fa4455d5b4ce635f9394f1ad4840e4686d80dd52696fc392c3eb89b7a536d", + "0xcfd7922b5650cab0a29e0468cf1e816f741b8b7af1768308f78ec0ac800870fd", + "0x5e14d14657d534de1846182929bcbe7c086d0bb9c418019193406ddaf34a7e5f", + "0xd727f88dbb4a62310cbdbdcbbe40023937a1f681b7d8f6186665129b9cbd32d1", + "0x5739e5a0f02a3239eb0f2b1242c7e8e0042bf7bede814cfdc6775e172d34bcde", + "0xd426e8bf6dc9171c6850a492b883bd4c92468b861f0c539ca890e444f854f44f", + "0x8f9b7d20a62ec75492dba773b54ed3416ff18adf070be3b3a35a803da5aba931", + "0x8af025719c903aa861de42fe4d082321f600c17e4b3a5f342ca408787957922d", + "0x43133e4fac22d85951ef650fada9a746b464a0659f370ee40ac6286d5ac3968d", + "0xd95ef78d2685255b58a7a0a581d953837b1d891f8764d2842c76137bb8f92628", + "0x5fc07f1fe21167cb65405edef11d30206ed7907d396252ed460365c9d6ea89fd", + "0x3b86a46c42d2ce31c39ac0bd352ac1feac8705e825c17b06d0f9db4f15f0b793", + "0x3672191b6d718f4ffe5e17039c25be00b258336cdf959abcb1615d7e77fbf415", + "0x017faf0c950fd86809e7a0acf0d7dea5b13d41b21c60d4844ce57459af471963", + "0x5236905646defeaf3666262c1781392fbafc496693b0f26167e14622ffd5e224", + "0xac45332d1c62566039b653fe67725544d531d0d0d385cb31d18d461e36ff3c4b", + "0x2ddde66714e4fefcf8d1130e2847d92bc758b39fe7a2b87dcecf9b3d0d1bc5f6", + "0xe8c6c341b96f9771c5897a0423dec64b2d3f57aa5923b0f694988302c99c1864", + "0xd31b332e66e44f8804ab8d224d179b3dc20f718dcc6b254273f2539311ae9038", + "0x2776a9d5ded89c9a8bb243b41bda3760dc173c41f16bc477a0c6e2de420f7aea", + "0x19c9b95e82cd6277e16bdbb383593459eae5584e8b746de7262b447f313f1052", + "0x6d602a973ee15804f76848823844da1ded9e2fe49b689e4e4c62948bbfb699ed", + "0xf8feaafdb855524369a0d0f011cee5f545b7e2dfc210148b13f84e0cfc5a7a49", + "0xe242c86f0d4421c505b4722b38c13e73c760d0d82bfb77fe6677a738e3f19679", + "0xe1d5352fdf59b76318738f0932c4960a590e46a0073694d9518a6e8a779206e6", + "0x7db333e369dc65cd5f8718ef0cbf38def00cd60bcc47c1ce2034203c6b2f8b38", + "0x1a57adefa64ef6f07306db76e9425624d106e73bc554822cb99b4697bb317f02", + "0x4a6acaff19b541b0b2fd43d9165f179fbefd353f8fda107d53926e71ecf68a8b", + "0xe5e886e9b31a3378ba585b16dc81f0d5cad83f4fc5aae85945e9a8898dd038c7", + "0x4d59ae8cb31c36c39c2aec16ff682862c097aadc499da6de1ffa72a6ed87b4d2", + "0xb92d833f46038c267f09c500c2ae557810709d7db9bd210ad4df55c65bc0826d", + "0xf1b971e2530edc4703ceb21b58a750a37399c05bec8a02409895af98df7e5e6d", + "0xbf1508b24e5d01809d39c110816f3ad5d703ebaeca4f218b24925dd9577fad40", + "0x2e25a66f7a580845ecf04e66fdb0ddc0e17237f6812f5f2a1640c3511c9f9db4", + "0x5e4f36c5ec099fbc35ca767f548304f8631700326b3a4497f8977d72c3727f13", + "0xaba60fa3aa0661858a1143d01d9408834c2be440cf06d72a93e4df1df6617853", + "0x42d149eec229a03ad942d1745ec4742f9f39a03534df077977f3f4691c939eb3", + "0x6b276de3cd30f0d7659292ba6e8bea190cc24badf88c10bf269fb4d6927b16e0", + "0xd978546ce32bc1123773b6562ae0a7a55b7d82c72f2aef2a56394c9940e7cd12", + "0x297a80b1aad7e95f57ba4f31c4835728a48f7af0e9adfa10d40fae8bd18cce12", + "0xef0d03d9db22500fae80ddba96bed81bf4769e645b25dd717c5ab0748e3da691", + "0x1a094a2ca17fad3f0e2703cd172bcdf4a0ce28e01840655a0bd19cd398153601", + "0x78f08815cffd7e21af3392d2ba9ce33335d8f25758631f9eb6352b826723d093", + "0x827646562aaa1ff4a12d97e59da6aa7c9166418dd2ba204e448bb12b321b7be7", + "0x5c0e92edb551cc6d182b35b9408d2538aa982b105003606575b5514c8e40729f", + "0x416dc98309f2e650eaf9a122508c163cc9e3c9d719812e0a6388b84fd48016ce", + "0x4f8382c1eee160e6877d95ce97b3a53cf7607c59483176cf55b40b47d198efcc", + "0x8acbe66fee2f75c73255b39489f15e6eaee75585426406d96ce786bf53076b6e", + "0x74ab3f7057275e0769c3356bb7515f108c69b42f4501a9d3873433e22639a240", + "0xe0bb35e880ee793203ad5ea844081b6fa99eeed85f570b6bfbbc0efbdd7f79ce", + "0x5761b5e7f8872c8cc13278f3c2ff836c325f4179b89897d0c5b6846752c312dd", + "0xeda698bc3ec12d8004406a7c7c9e3a16fa085a8448901352ce3713efdac58930", + "0x22138b1713dd83a25b1434813492910fa8e54d8feb9eebe1aa5d2bcb6ec2c69f", + "0x4ce3cfc5b1fe6f87e9830840e85b81ff6117ab62300953524c39bfdbf7868980", + "0x6a6c7372b5c90808a6a966c0b90419b5efc739e5ad5ca1fd0d6c9583ac37f240", + "0x257fb60e826d4a92c030a645375fb46b3790c5b318b56b1a299e09e0e7ee3911", + "0x5f577cc4d21c818bd18e427e6d9ebd8f207fc71ad5f883d0e24eb9f856e3e10a", + "0x0f387595af36d3d5df451ff3de879016c6cf575fc1f02d40d547ee91f6aa36c5", + "0xd8ae5a19d1327025446b5e16d0585740839bf32d9c5798bd39e3e0697113efac", + "0x10e80ae2881a780061bac8bae4514f0df0077152639aa0da1b9d8b3ae234e184", + "0x377f9a9fc5a6fdc69cc76f6f36282cedbe9c83bc43dc345fb49db28ef6b37cf9", + "0x2d52e3af5f7ca108b3ac7cfc495b96e05e2d7f82f7f83ee3234c84b77a1874ab", + "0xaad9190dc0748ae3f78e9e5a3b4ae10eb6bb0b2617cc2d16ad3806bdd994688c", + "0x83b863f21344a6a576f805ed480eb72d3ae3dd06a2979b539a43f2c955429a24", + "0xd4d0622b957b87f71610bf3af65ab018360d320ebf328222df10899830ca33a0", + "0x7ca92b8bf0337d0bc56aab4c1a57e29addf6f0a4b9070482f06a7ee89ccbde84", + "0xcb9c71abfdb931e30a67e7b77d247be70b005076b64fb138be12f260f9c73334", + "0xfe44663a9c2067fe4bb8b43d241438cc63af9638cc1859b5d4c7c7f89318e558", + "0xc962b737dd9438cff5272e16038ef0ca55c4e1b5497d75ef5a0aa53ac6c09448", + "0x2b3a80ef8b20644facd31b5d1618814507c8f603dd8079ba40da09b550f3a2ad", + "0x4664f49d6f114ae491a0364d007724cffe144f4085d91f24f8d3fe87f5b8e393", + "0x1e2456e2e055decfa092a838d2052b12527c6e56b43a787518b121b61f7e316b", + "0x2f45b1ef6d6ae7196818f81278f226f3e0fd535d3a27b63497a6fd208f195c0b", + "0xaf265121e6406f84f7711447d3118c02b446c0d6db9c4ef266208adaa1acc0ec", + "0xbdf275c92443f0755251473e89ff46d741681c7eb2e7811eae25a0f1853c0bc3", + "0x5adf59b0a9cc02fb5bb5c469a52582efec5c317ee1896d8df49d15c18db944d5", + "0xd9aec0aa26bdd240b709a688a0b0565f3543a9b3686b78efeaf070140f2eaef2", + "0x1f4890ecb2250b7ca57a092a0a40b0e4b0abeceaac015189e0f6e9486ce4bc7b", + "0xb0be545c813daf899627b982dae9edd9de72ae54094dd8eb25570423e5c5dbf6", + "0x0f98f4ac26d2966ff4b129950dfcb287098fef2e60c3bfeb1156425fab8db92b", + "0x5622210376d1016d0b7615552cf3d66cbbc2d39d3eeaa2bb100cc2328a81cd95", + "0xe3523e80affdd0ccc3090ce202e7aa408ebaea9fcaa1e705242b948eb15f3cca", + "0x98154cd06a09aa5f9d3e4b4c824f1efdf6eb5ad4b5c0deb1a16a8372bd073a94", + "0x6680dde58fd0b4ee4626a8564f827c8c2cb9c7d0ceb4437412f30a15ad486689", + "0x61f7582008e42c3bedf33342c9c47003a6ce9264c977204ab5ef778f6e6d234b", + "0xa8ffa5ea4717d9f8d55db7d5626799aa102f1e340d80c68896a14204ac38c8f2", + "0xb83aed2db0ce9eb79c45259f66d72cd00546caf8e39e5efe91bd03ec64cf96ee", + "0xae616f4c1416b46a21390e0bd5336935490b9894eaa7c653fd9909086a1df5be", + "0x18f84c5cbad9477e0571574a0bed0714d3fa587ca21e39839a95752e6e3f0299", + "0xdd5d5c64b54437f649d7004d54863160da8db8518ccc99ddaf6942fb551dd798", + "0x72b7fd1a0758b72010c216bc7394a19c1a0cb6620386a8a91c21d3db69004b49", + "0x2dafd23aeae504b4ebd17ed518648939cd63f5e0e7448bd8c0fcc78dceec4059", + "0x787827158ba2f53c21c6d05eeca56fd0fc2fc08026bf09c76cb17583340944e4", + "0xb1a4b60e4d00c8189d940ed2fc070c1238d03d58d572ddd029ae794e47352df5", + "0x33e1420671ccba2d938f5b75d45bd31d90c5b424d9a02e7e7eb4ca60ae402443", + "0x094b7129d1087747705a683bbbe61b13c546decbed3953c7867aef485865c7df", + "0xde05a46ee5e26ff79801bf32d1f4bb4dab2127ea60f7bb037dfafbc16678e1d0", + "0xc81b90e47d6aae3641ddbfade2f8146324456ef372da470b319528114131743b", + "0x2b835f2a36503a9ef3591f1913924ab134d3fba8c95153f1cc80d503c74a1af1", + "0xfe99568c1fc3808ad05d99fe2261b27308c97bb39a965f96119e012ca8ae63e1", + "0x2bc9ac9d5a461286cbfbdc7d6181c795321ea0779835322b5e09381db7169be6", + "0x6cf2449053f134a8ae48a4f0007ac1f8f39d30d9c3269155b8922581a64b5d43", + "0x7ff755adbbfee435992589e1eb09e92532624a9888375497ab3f46d921aa3aad", + "0xe708615d47cd53d7856688d2ce264311f7f94631f318588726b88ef29dafd93b", + "0x96e1a15735e93923060b3954925e8d52b0a4b5cf6516190efb907bdfe6bc34ec", + "0xd33e1c73fa37307c1199ee2dcdf3a0e92152f3f6f9f667ebafa190837a77cc66", + "0x57fa01905f12ba8decb5f5beda857a607e32e9781d39a46001679f537d57391b", + "0x2e5f2df2fb0d327f4b574c0a37c243b7fb4210af7a0796cc2c880252ad8438cc", + "0x26714de3dfeb00f0d8c27bc6729b89d3b55b82cc1eed2999f742c89fab424fb5", + "0x7bd11bfbafe1e87b58428db1f819f4ff2455a4f90db5e60e3d84b28d818a1173", + "0xfcf5fc7965f836dd04e54a9ce577aad76d10464b678259ef1c8bc2a0a0c67723", + "0xdb2e6587cea3bcc6f109e350d6c138cf919229e884a2597e4d6e599c26625736", + "0xd23ff7e12ed5f3177dfdbb51edba35d879fda9fc4ecb2c6f89bbd6addb772697", + "0x0a64a15947b2f5a87070fc3b8171485aac3e131619fb8c88b6bb2cdc1969a5f2", + "0x17d617aa1c6ccbd70ead8237a312152f42926333e8d9165266c497ee9a18da61", + "0x74082588643fb771a733c66b4ddbefcb50daf6742323e05ef4a2604c5d56eb83", + "0xa2cbc98dde5cc72ef8b0d222037f01d308a550ede1f3097c4bf7fa3e36914258", + "0xb7e7d0fc915050bcb17f685f798292ce93d3776096c2b3a252f0fafb4c799230", + "0x6f4f736ebd9941ebb79da6456d8ad423a3711f26d3cb643b81c8e02fc5689af1", + "0x21a21b15bf03238f62f610971eaa96dc5dd41ad45a6be2867afe6206f7ddaa20", + "0x459ef1b2c13a6bf1ad48e62b2020851a4505bcb88923bf0d5ec2408c309b973a", + "0xccb6429f43f6447cb714d4bed7e003239ff6926804185c6c51aabab4b355939f", + "0x5e580a7635cbfb695c97fd68a118ea37680c7408f1504b723189470826016f8d", + "0xc523d6c489334472d1af61a7ba1732195edc25bbf68eb821d1acb14ed5c98663", + "0x31a01c69fa6a86680831350cb67fd5fa06fe8accbd5cb6e2ce0ec689dbb23114", + "0x4fd4d85570c94d03c49e9aa60cd6bd6e684bc77d3b04eb5fc6b2b7f206ba9bc1", + "0x29b8a2ddb612c8c135eeb9f3d8791b603e7bf4d860c0d0b6bab53deb22b2070c", + "0xb156a9272bc83b3b85b93ae2c43f7accaceec85e1d77ddb1343b18356e00d572", + "0xcb08ef4355bb9e2ecc478bb4e80425acb497650f20509e10e134f3f60b216866", + "0xe2f0614ef7265b21deeda6d174341d59b7d13655ee7d473e5c4636dcf788d674", + "0x0727823720192b8a7a66ff04cf1913a31f0dd5b71dc145840533c30e0b7eed06", + "0x4b321b6181092414a3816b076bc86551fb8763ddb401a6923daa9c54c788ec5b", + "0x3831bf3c54c1aad7f557b8c914dc6e3d4c106169175d1e988eb028d160d49473", + "0x95d94729d493223bbd8649cb5117f14c279fac44fe40d1de1e82b7bd1d9261ea", + "0xe2632a472234dc31a473885d7a72e84c24f7883069afed9dbcb643cc5c32af55", + "0xc9bfa80366e9c466df9cda1b1c97de5f627a0142552e97f0554e597b8bc6563f", + "0x345a832166ef2f750ff143a57fadcb439233cb8292124d20bdcc294fafc5cb92", + "0xf8e032c077431b49212307adb00743b054e996e6d3dd101a40959cf19b9ec796", + "0x2243b4956a00a8d2d09275010330f4268d2f536cfa14741313231e6a7b33f445", + "0x12e0f3ff844f21b058d0e311e266cae6d0ab5dce8952d6133a95fb8659e337a7", + "0xf6d80062e8efaef3021b7a747220e713003c37ec733ff8cd15ace6b58ce60fa7", + "0x58c2f923852b77377eb36cb9c26f1b73be98a55c9b0dc65982098e5580ac8e26", + "0xb517d1be70a5d39a4da4ce46821d678257836e6f70621f529c4019d974eaaedd", + "0xa25aa7b0b4c5a3a0f063d4d2493d8b9ab46afc091161df9b1840f47d42334433", + "0xe1795c2a1fc25ea19e6599b070aa9209f2e89a10f0524d8aa78e921c12251514", + "0xdbec5c0577039e79efcad6ba30e0693127b50dd3b108281cfbb4adee3f212ca1", + "0x2c4835b03837ea2310484879212f94fdac6bd2dafbee292ed67f55851053f969", + "0xe0d9e5d1f86eb15c09f41181c96d64fa1ce3a9c69b3ceadad476353e0de1423c", + "0x2a0cff721302a055d3aefdfce14020b3f2418110084c0b1ac1c30a07e3ce988c", + "0xc8ab63d730c578afbb7611167850159fed6d1e73fc3c0b406db19934073ac0ca", + "0x3855c134a5a94513c46470411794ed81d83b29a8da353709035f6e224a9da80e", + "0x6b54eb434f5cb21a1e01b9f067647b857d9b236044220dcfadca3617ef2b568a", + "0x710ae824655688ac255d8046d8fd8438b110d768bdf9bcf4debc9381e02e873c", + "0xde3196ef84179c193b82b1175a417820a9403bdc06c4bfbb5010c8d83172edbd", + "0x5f4e1710e4896439b22f34cf4485b27edf1c35a2b49328eb8f018cb690d4ab04", + "0x37aaadc6f1e57a61a8fd925a6b583105eac77105e167735fc117b347ca58ef47", + "0xc02093ebafb3fc424cef40f5d8699681a4ef9fcad5c8b52002bd911f23812a58", + "0x34d274e1699647b5883100c125da1fce744238bdfe4e1ca2a694a5efb03a44eb", + "0xc14618c6491fe32dcc5848c2ae2a1aa26832343a1ca21bf327ae2a0ad182c85d", + "0x434b07e53641dd82eec95f4b472c1d1d9855ef8492ccd53137ecc82b2bfa5845", + "0xef6763df32b5ab25d8371067949efbdb7f2b9924685918ce3d8068766d6e729d", + "0xe534aacf1e9b44d4464b82ee17ed9cde79ca3a574c5869ff5b6f44d292d1a926", + "0x45ee62bf59e4bfcc76208dce86cc8234727de291de27a69a72bf15ce78488a1d", + "0xb170601af6545cbbab9d707a4623a07b9cb69471f994b769bb534338d3b556d4", + "0x73c8e569840ff39b4dbb24a7adc678e1e7b63621aa77a5533ca968e260d8b638", + "0xad8002631bf3e364dd09f094028a9f4faa5c67ad311838cf65215d6a17d58a04", + "0xee81a28db1d369d17ae97346196672e6f00fd622865cd618ad8447acab9bea7c", + "0xa8caab152258db32f3f03f44ce090e3759538b33d5e557f4a9a4169a0d436754", + "0xfc037393a2fc3a24a567923bca89a7714c72c742c1c466dc0d47296eb6d81b2c", + "0x1ff9e9455c7aa193842f453554c89ab25dd9033b0f7dce5c795e19b1c84b44f8", + "0x894d063e6911b107a6a42a542d99bca6f77192a0852029c0b5cd31a47c8bd2fa", + "0x6df6ca69e3a40334a4c081d74474e00c98ad6793f5cc58b5520be41eff4b17cb", + "0x688504ac6a0ae42368f496f0651e4c1f8ce13fa707a6b98eb52c1f264eac14b3", + "0x6f200669f73cfce5f2f0d9318673ed75c38371476e94ec96dbfcc683aa583816", + "0xff4a6e25786931e08a89007848dba7f62f84a776f7d926bd6983c2d9dd3a14a6", + "0x496c57858748bccddfeee4c5e496119f29a627a659349a4f8f65d1016e92d841", + "0xa5fefc6e3ea89e87875efe37fa9d796f678c6abfdd651897184b52d966fbdbc1", + "0x08e6f4c847dd3aff0e6e45c63939869cf7b007f712ffd79a16313eebf77eb4b1", + "0x00351e602b0508184ea47bad9e47b77071640422296e6574e1ab52cc02552a59", + "0xe1980e52f5fd5b81c96c6c9f1e6baf14bac191388d7e29e1191110fe68d6c88f", + "0x2ba58560d35e709c358a6caa9347c51961642756f4cd28d09b8aeaa532f7c382", + "0x3b8c4425cf002597f470d30c40ca0e40f019d72e33ca6ac54a5c80509bb4e45e", + "0x25da4aab586d49e195e1f9e0d8371d3368e88e3801ab9db750a40df31b2556ac", + "0x9b2693658ec603d976ec25a76c8ffb492d0647581da186a5b7b49bc6c18eafb8", + "0xd27630e4ef91c7144b5db0de4fd83fa53aafc8a463991819a919916fccb66aa5", + "0xd296657df9243ccfa00571774c0bfc0b85a64ceba954b876cb40713aca41d24f", + "0x9cd59fb8b4582d88a9af204976da07e57e066d48bc15b7c707c17dd11536f735", + "0xd6789d7ca6f21a983e7ff5baddd5c7743bf9a76419136384662a685060f16eb0", + "0x38e752a76cf82b63556556042e8cd6e4304d08338aea8746c55ee5864eae341c", + "0x56efa0c6d7d82b0d6b9ef82ea4b8d792d2786ac54b001d77e8493129accec545", + "0x9bb4717f3ab9f8d8a8fa059ef6d09716dea547ad0a1d99eb41d5ef3d21a46d78", + "0x2a616d50d6c4199351074e4bfb6701c3a8a6a65a235a7de581fcf40c6b50eff5", + "0x5a7f0e8b42847b84813d9129819636a60d7f50271f4c1dfac09b7bac0e00be54", + "0xd046e59d18b22e4ad6b9c83a41599afcd2d89d6bfa59f95dae874e8491b1469f", + "0x851c439d105b3622e675f986d249065d7936a7013a05d920bef87e44bc2bb08e", + "0x8e8c4e96b687a26e22a340465a8334fb8594544e7cd652f0e667ae5849cc082b", + "0x355ef21e4e57693374780b2a871b20f361f472074e938e9f33e7b4ebbfd9f447", + "0xbc97e95990cd4a23b97759f2a5ccc45359118ba88dd99895b2e5a3736753b756", + "0xf01b5e69d8d5871473833acaca6d2a1adaea66b76aec97e258bf72f50bcdec41", + "0x3eef80b3124ee89c5fda3a87aeb870dc5fbc781df98e7b0fc1a868acc3b01259", + "0x218a98c02e2b81ccef1bfc460703e57eb8ee5696553cab7b8ce9c95da70ea058", + "0xed6a555d0a858e362e15791f49cca517b74f0f764d8b032c0278becacb694739", + "0x23d9e9207f88148f7648c0955bcccae4138b029ed5b4bf2d574987a568d8283a", + "0x28af6fc0dcb7f57f8541a8b2bb3e781a0ff4d9020cdf26a8119a93401d45eab7", + "0x01989d5a75479a5a6eeb10430746b76a639a9716b8557b7864a1b7b778d265bf", + "0x475611d8792a80560752a9573f864f9ef3153a4c30bb16f7bf1bccb69241a8a4", + "0x0e52e291214aa6300ab1d875db31fbe6274d9769cf7dabef852b5d847e3c6a2b", + "0x04ea3fdf2d26c297c4276344bfc70b4a7645bb22382127f8ce9a621967a54ae4", + "0x64694ef5067706b01581730944a112fedcca32ce9e9103119d9ea4b4b75efd23", + "0xf37ab55394c6233443e1341d03ff07009f1b0771d12b202a78e2789a97e83c7f", + "0x4d723e3c6d46625adfaeccdd1d8bc3811539a7b658050ea0bc9dec625f180df3", + "0xf44e4c0d2e30b8bf0aad255325dba7e2093bb518b56e9c89625f67d5c139a65d", + "0xcd9bdda98c027168fcba6eed7c551291b59613aac11e6e02a00989c93a13225c", + "0x5fae33477d301fda6e6e0e29bb6ddf39c1720aacdf33a4b854806f8a09cf5e6b", + "0x464ae4d9549ea34f25c28ce8daae16fc129adc7ecf71527cfe6df6e1fed72234", + "0x01079ed69a40bd07152e241433be91d61bc1338d83a686d619fa1a21e8c57727", + "0x1f36d242a382eadc149704d8d525e4ac25e0fd2741876022e8bb474d3c726ce2", + "0xe54d3b597d9e37ff213472332bf31a0b16ffc809540c593b1478666019205dd9", + "0x24fd9f8ef011c060d698711a163444b20dde695a1b0db81dc3dc171ad227619a", + "0x01fe14f246bcec9826c6b5dd668b256baba5939749ed90932a603b1cab47fdc8", + "0x6e4f521a4824ae2306c046f5545c2f7ce911104d9ac1204095b8951ef5d63cb5", + "0xd8f0f8bfa160374ebfc90fa7966df4391c1aecea802e3b04a03c087080f01fb0", + "0xb4720294d8b145f29af00f220e076b271b587e9af98653efa47e2d47bbe6d95e", + "0x93223951fd8a31a3e6c98fa17cd33086e8155779930440a7c2fd25eca31f682e", + "0x752fb11e15acc591e78509137beb18cbf177877e4a9846bd33acb1a6426d1999", + "0x3304e2f07aafd6b10cdce727c41ed85ce0fb80e59fee6b96b9a2e98f73280979", + "0xa756425b9dedd5e6c2197ab4a76c14e76b250d7ce4e2b8ada00fc61e9f62dae7", + "0xdcc034de60f855c63dd2c5a9cd4930a45a54e416558c6945cf8e058d55886d32", + "0x77ab21e42cd147f95466d22f5e95e965e38ff22938eaa6b5bca2810d6a9e2e4a", + "0x65e59fd9132b911d65da3c1094a6b8523ed5d9b7eb8822b8d2273b55ce4aac61", + "0xff6aba7d3fd54e3948829cb59ae47084eb809481665c5c09300b834567ee4b66", + "0x0a2b95075de7b0579ccc23f174b3fdf6a4ce6aa9a42f1ace679953fba0956d41", + "0xcc6f0e601cfdbb0631c13df4decb454d5cbd554363694faeaccd4edf16a9ec26", + "0xf2937d3b0c62b8f47cbac34a29d0a7fe92ddda227f984ab360521d5168274583", + "0x9f0e54b594397e29868adafea8686c5e3536fe8ea767274e277d77a5ef2917c5", + "0xfb8ec9c26eae16db5dcd2f05476d89416d2c56a841157685dbd1653274ded2e7", + "0x07d71ac9ba4b3f0a616205d182155d7a34b2463c2db95a47b8d03a45382b8e18", + "0x6e5d854c2848a36b6feef20c80a87a88a40af50888e787652ca54601a7d37d90", + "0xf9bb21fa1e3d6af9962a9946d7fa4430708193335cf32766ed56aca50fc368b9", + "0x7750970996ed6e687ca27f08481be0fd426318aad87cdba015ce129dc896b41b", + "0x4ff8c843185d1b5b4291788c869ba3723969d2dde2d74a9b8fe27ba71da67ae5", + "0xd5730d698ac2ab71bc360f24bcd7a71b8001ddc86205d7274e636bda83d8e3a3", + "0xde41dece62466d3ca8700150723f0782fd624162030086be716485e12d5a93bc", + "0x7a709bc4ed84e8bf45818e0e918115d686504fa0dce6a3bf8513d0bd488dd39e", + "0x620fe2d0affe0c46bbd0b74ab6d7c3bc6b4950915cb83742c832c6117ea42347", + "0x542ee63637f2115e11a3e132bb4246d809232772e08b23401eb8a8f0067a4ed9", + "0xa284a3dfebff214c436ad67d8a36162a1810bd7a51387ab68188267a1e166d2b", + "0x0ea68982e12031ad38aff543ca7d623ca8945e649527eef2afca28993de42ebc", + "0x1249a7d56112d4146105a1f257bfd399f18aaaa94ed5198df8634c89c807d4c3", + "0xa691469bf6b8f7caa8559865c0c99f77350f41e0de9851dc887d850be5809cf6", + "0x34ee8cca77c5b32a79cc0a6811cc73910cd0353fb860171443f08d17e3f18174", + "0x73a7b7f8bb18df6038c0df35ee55b0fcb4735c7e0977efc98008c710530216ba", + "0x0a8d0b414621d022fbaf4a1c0a07169e879a857fb105637cd8193633ce755f11", + "0x36f21a7a7d142d026e9222b884b583387f10ba692b1cc9232a32ab401affd684", + "0xb5b69e29e0558368a8abc4ff3ca022d158fb11ac6f2138cbe0be84b387e1ab7c", + "0xa57d1845ab38ef86763a374b0323b7ff84722941b06dde3c5cec61de40c9afca", + "0xe8063bccc4fec0a51a6d57ff999a7169e15d93c0d7cc3ed57c065cf8afa912f9", + "0xe9b89463cfd719b357f878a927bbc72ca3d06f8b88f9a5c8568ef2afd3118ed0", + "0x4f2ae01579376ef5a2ac6e849cb2d3befdc4e5e814a5b8de31941183697e51dc", + "0x6356b87dcc8f1c6c83a2601be987b44f77180da8b8d8186e6c61eec301ffab06", + "0x4f2918a79394961e5802b8aed07d89b3b5b459a3d9706f0749ec65f3b3d9ebc9", + "0x145016d42f0052f68aae089538ffa956cfb2c5a0f26763fdaa940e427bea7ff8", + "0x897e9dd36d2c12e8941763ac6090321db8cf73de4319c99f5e4343d5eacc856f", + "0x7c1cafd66922a53506f93d8aeb80dfacc9da69e6b73cd3e4a9779cfee5e4f64b", + "0xbd76e3eb8ceeb1f47ed865351b54cf491173ef562a51f068966b71cd294766ee", + "0x600135fd75adae0801296f92e1a7219287ec588fb4627d18095887da1c71ee2d", + "0x1bbd55de061070b1060b4184583e9b5b2e307197235c986dc1a3b942f64cb107", + "0x07e182e958faeacdecdc43de50c8d4226c6f697d7dd4e2ca9754687605719068", + "0x3591aa6b10db91679824f182cb58ecbce7210444314eb4b4e657cbe6518a55a6", + "0x8948a1a48823911e4e16b54b3cf789c8b04a3c26008f97f3c71577a22c23cb02", + "0xd37594cbc0353af27e1e70e249db20f3f16bbdcdbfee1ded9bbbb178c3454d3d", + "0x639159845a1397fe73d7ab041d7d9a74b99ad8009e3d63d37f329df35afcec0d", + "0xe41e444d9f8b0daec348208914d665db316136f42614013a35e3c7e97e219dc7", + "0x2ed6880fd4b6631ee593d3161dddf19021a1365650009d01e264243e032f4f6e", + "0xa6e5eb641f39345184365aad2c9258d21ef4b7c65ee5dba93825aba5e8e481c4", + "0xe2880fe0d6e512ae4e0e78a9249f6319407ec2def2c5745292b750d16cc41786", + "0x54729b299e086a2ce795d339e5e20cf87afdfeaab0dc6fbbd0ce5f55194c319d", + "0xd3d3dc4b51e0d0487c9a09281e1115f593bd24cd09a113c0f1a89468ca1ea2b5", + "0xc86f8dc093f2adaf17fbeda345eb2f4ace3df6d5bde0c99e85200eae84cb892c", + "0x84ba70a7754f31ae366693375c5daf4fa38f7f086791c1aded55198adfb91392", + "0xc7ccc21a8a1b96591c32e98d2c7ad358279c2c1bf6bf51dadb18cb9504a1d751", + "0xdce9df5d5bc2852e8fa61e254a9aff0f03628659df28f51dca37a5a6816bc479", + "0x6d0a555491a6fb3f592fad8b97754d02bc6166257e5a83131fe9d859e05d1531", + "0xdf805b41cf7c7b9346ed152953e6334cca6725a6428e81b9fa4227329d231ce1", + "0xf94a98ffcfd4e28d863e790d1d6f507445f9e64165b3937b0c40af11789d5296", + "0x56723b13f5112f637e1e84c18bc3dafcdbc40508bb6a88c68f3a46e56f5783d6", + "0xd550a9d0bbb3745e7f5b7e290f6755b030cab20ecb6f5daf701dd3850737692f", + "0x7d88f7a5af354de5678a270b8083ce441be4c1bd6875a337b244d93bd3cf0d2b", + "0x52b48af317cb248300cebad6fa682a741f3dde7da4bf0e7c4162f9f1607e7d68", + "0x04a7147b282fead783019f3453ad8d95618f416eb7ac0d80cbe3be8f68a13c31", + "0x4be24f18efeb4c1f8b12f124bd1f48d3edd1777629094292616bd89ad580682e", + "0x146f8719d2e7f74deb3619ce6dbb782850431bd5a5fdf5f5d2dd33a54874f536", + "0x6497f392deeeb3359e35622360f6416da4d0986ef0770ba22876cf1990e5903d", + "0x858d7fc84e4504c9e5f54cca2c9facdacd309d742aa7657c6f90e25e4d7b6f84", + "0x6fc455af373dfa77825bbaa65b1539e9eda251850479b83db15b9be2b5e144c4", + "0x3893304198a242335c96423344d68ffeda11c17c9b6c5785bc59482c385f60d4", + "0x3b2a22d6d87679b173a1e82db970e6cec44179be8218489a8aa863b7603adc3d", + "0x52534879ad0d442147ca743ea21fb5dde78510c11da353fc21636f9725f9609a", + "0x194e094c77eaf3973447de9faa29007b7b895e3c855cd01c0d5609e478df10ae", + "0x64ca084e56ecda7e9f2a10b0b2262f29d5bb8bd361019367dc41bc8f97963961", + "0x05754bcc136f5b541e59f1c40c99572fe188f6f2eec3d91989072ff3f3c1ceee", + "0xaad0bc1df434492344bf9351345d3288340aff851f687f952e84d968e1a29afa", + "0x90c5f0e124e125b2fae5d2ae5c8a1f5fe065ac2cdaf62829733599c69f66d674", + "0xc20d9e82fda10425082d631b6705815ac17aea924aa2ca534066d76734ed1bdf", + "0x68fc07e734ecb1c3951262cf804b831ff32e285246ebeffda3949e62d31d937d", + "0x7e472ad7b556415586fb5950e8d0b85e895b8c9c7fa31952936247c8117fa0b6", + "0xf5c3457576bf1b624f0f558bdba237ec349349c60926eff5c3ba8c55b32fd6be", + "0xf9c3dd5d7fcadfe33b2dabfcc4f732661985a72166afb8d16486028b89f35bc2", + "0xa493336b47637629c4bbbb89e8c8e1272722db0f7f781f4f78185d6125f0e5a9", + "0xc8cab7f153c08cdc9607263c5129fa8278a7228188c950806d5732db541cc59e", + "0x85c0c18f11c32eac54537d2c492eb9cabea9a39d2e1ac56d8087c02f68f64bd0", + "0x26a001beca07a641ad1e3ea64c63e395c436836d098818f937b4354b7a925122", + "0x75cdeed9ee0eacc02eef853a54a6f0201693cd1a60d26e504d5ef16110ede59a", + "0x8e4a5fd1a18f9513cf70dcfedeea862dfa2b61cbee80fc204bb0315a2c051424", + "0xa42896ce30565041da64ffff0a80ee6a6e280d0459e5c29c0fa3a79587d4a330", + "0xe65f4193189ca1da6d6fefef1c2f14bb340e56271b2bae3024dce98dea451cca", + "0x48c844ef658f2270efa4be7b85879cdaa2d7cb27f5fe9cdce0ec4389a5801707", + "0xedf37c427ac85a7c317bce25f06acf626cbe86e79f99f148667a95c03ccd34c9", + "0xbeca93d3b7e0f9d2f2a6b9b9ff7fcfff658c0d417830e46a3aa233f31908782c", + "0x61dac332da33e3533b9eecc1a287e732cfb9a666ef32de6bf2d2706792264927", + "0x622624f3810a1e24d48f54082f0df3ab3227c9cf9d39b007875a3428e2d70208", + "0xc1276025a1c848b7dbdd6f4dc1b7b2f4b19dcc7b0e312897758ca538ea334bff", + "0xefa6b7d35c2aeebf0d472745aaf6d4118279a88273b7513d5a21c3f619af8f76", + "0x2250fd4931ae5059e99572a7bfcee0117090c362075c0d90eda1ba2be2c864bb", + "0xe9b7860868856d1ba5d17cfcc78020485a36814482e1e8812555094dd4e85ff0", + "0x4df6b40297b258ae69a8858e54ea4f45d58b0ab1b0f1b35c3bcc4f96c928c58a", + "0xd17bcb3447f309b3ce0a48b651de3d979810cd9cb7edecb9b44366644e25c729", + "0x7292155c0cf9a6466c80ac2930f8305cf9458f64f40523a6b5e08d2ddc180080", + "0x536bc35368df05c3d3773b27110217c2df04d2f86e6e8a8dbf9dcd13cbbcb984", + "0x0f4dd55ebf1e1ea736108e62612972036dc533bc4e52a8480e3c875a7961ef69", + "0x902334a20d083b4634a79aae1a2b4158df3832d4c7f9105e8aeb8e38ac0be589", + "0xbbce2d76e50b1150179456ec4ce4faabcddc8700435675ebe94204325ee6f710", + "0x6cc4afc371970811c0cbeed73acc72641066edf1d81a585f28987ca1c11e8ed8", + "0x7cf1f9f26bdc8c7ca419400a03d683564bd5947068e4a01670200358d339b12b", + "0xf1e37f56a2eefc9688ebd8a95772f98b9f995e3fa9ba6601095f973cd34a13aa", + "0xfd7ee922a80adc2f6719e6e1e90add658033c7165441e399a3f61e93ed56af2b", + "0xd8ee732eedd804de6063af6b828fd6c4d9496115a367cfdd424a035646cc9e02", + "0xbf6658fb5940aabd9cf82fd9e808c9a23d04d171a62d0601d2c9dd67009f0b36", + "0xaaf7099280336cb78b87d9cc3c06694c4a66aea2a095ed614a4578fa8d61cbf9", + "0x46e23a8b56b9061bb89a42253998e7fda17289a12c63fd239a0c2866df529f3f", + "0x19b3323694c1dc611fe3722987d1a927dbf1a1ccd211b3c148798cce7b814fea", + "0xc16ac7ee79c7ac2223f79bd97a193c3bb68f609affa1596b061e9ba32a4978b2", + "0x5b929b2451c9c284c40c745bd441e1db499e336ea8a0b616e925ae780a6553bd", + "0x954a69859410a4a8309ddedda3f1af305c0baf02b1c0cffb27c0f89e629e4dac", + "0xff44052a4c53837e32b9bf4bc6080e78d3a78108a69100bcb05a85d4cf9e65e7", + "0x6199a3fdaf20c64b9587cf303682571f0de0bfcbf6ae34b4cc20485228a39043", + "0x0472d6b6c1efa88008d9931b3a1348a8892a61897e8634f2416758667da516f7", + "0x566bfd31096a3829990cc587e64e2a4477349e91b1b37ad6100be6b3de6250b9", + "0x5f51ac85ca1ebfbd382b4d60281d3746d98950aec6cd6bc1ca5ab22eff929a20", + "0xd45228a818a9f5d8325d2fe4873ddf5b0cccf4bc6ca907a08887adc19223936e", + "0x71d00ba260caf196d91b48601bb92052272067d1eddc4492f8709c3494ca253e", + "0xea6234921dde61659d45b0c36da969b7d3ed641c53cb54392e5a56da9f8df1d3", + "0x5a0dd2c3c892862da0f65bb4be26de9984c65aa2ff90526e6deaa1a3c079e7ac", + "0x18fde3435086b1d71fd5d24888c99bfcc2d4e7146ef4715192f4213ee9af70fa", + "0xbdf9a94e2cc27a784153c66df5d7f25316300d43f5a802cb7c5d6979a502c23b", + "0xb111d486bad8fddd2cd7d22e49d3ea3f4a0177af6fbebcdcff7f7f6585dc97ac", + "0x56921a22df9c98a49aa2602e551ac3e3d07f857faa228b56dd55ff3962f2646d", + "0xf8c7cc4743849e6cae415696d543445b22150afd4f4badbe9365f169a8aae3fb", + "0x8b9945119a385c746b1eae86d57e2a53453ab4183d49aea9412003a5e815ae8b", + "0x0dd84a3fbdb5a44b934a223503e9dcc73f893d35c77c3110c005398c640d011c", + "0x0960711a40b3c268874fa251c6c642c9e43ab14adf98daff1bc44949ca4a472d", + "0x4097f47259eea82fa8a531ec8c32b20b0064a0c47ecbeb1c426514498e5cb703", + "0xb17fab14bf8cc4aa51a1209711533b566b907d83f17b5ba62187681951307e74", + "0xcf522b0a5f9c427dcd8a7c6e358b7bea8d683bcb1fcee19fb3ecc05b85afe281", + "0x61eba4f90383706a2220ed3ba48020c14b3b6f1b76bedfdd3dd84cf66e7b93dc", + "0x112dffaef31440102f9660a5d73880a7df7b4cbf3c5189b9cc86c7641ac87f21", + "0x908edf9e1920e01592cc0478bc15011699771fba5a3624296521b1997e409d06", + "0x44636c1768f04f94d933c5d93679572d01e9d69f88251e67e8ccca7a50d63bb3", + "0x146add71dd815d75bd46324c8820e2c3d485567ad276f0ce1e2b63c294c989cc", + "0xc3cd14435b5cbd2657f38726908aefd18b21d9fcc82e80b7e1ab0be96a13a2a2", + "0x7d9cee568a4ecbf2d1f980d8946595bc599bf56ad6b07ffb2a83e3b479b1dd07", + "0xfafb0df45cb663db42cdbfb09f7381a49fabaf9266f1ff788dd31ce22c30bd55", + "0xd16d87925d29d26760244c938cefeb1366bf9551545342fdb7583325e08850f6", + "0xc6186d21f0f3391a8456d1b1753da24806d02cb9c3391a40c9fe84143d5ce31d", + "0x14b9cc8f7d3e37d7af07f80c72ae6528b326b146c6380ad0bbdd8fda89d225a4", + "0x5580ba4d7233e13cce87ba2e2607db7a4e87e571d5b1b1a75887f261e03d393a", + "0x07b144469f1d123ef7249ef2c445760a995c2c70f6e48e6e166d5f3508f4333b", + "0x0d883f633ceb91e413494b00e9dd563cdf121d2a2dd397baf4d29f7664633475", + "0xc619796115f48419208b0122a9b18a0bbefc05f8d96c39248973d66583ffd8dc", + "0x994e6fe6c27ba71693d04b2e02ba9c0d8679d1b10fbe733240ee5c323ac5f936", + "0x88b987ef2d0f823fcd17ed43ab28bdcdc23d23127e462ddafe56163e4be4a980", + "0x67e115f2ec5c6f6f7983b4bc2c2ecd21f639a8da639da2a594b018be2a3bfe64", + "0x8e4b8ff44b5028560b047780ce05353c94fba885e186b1b198cac5e90a6cb990", + "0x0a0fcd91048ef7e0833552ca444ed59d74fe7151190d57e5b7fddeb02f119300", + "0x9a5203fa1af89bb99c8d2228d396731ede004ae03afab9566195b89ddfa51a8e", + "0xd540a1122c44c8e728a78048884f79fbe5367873fcc73cc46a9d4b5b4848e3c9", + "0xbf8a42d56b46f6036c6382bd1e970b1d6b5f72ba54d1d5b8d0a3edaa165b7165", + "0xe0262a446e33431abd61bf3e199fca97a575c7ccb97710c2a1847188157909f6", + "0xe7f717feae9bdcd172fa4b6b701f0ca5b58671f9608b5b5a01d51a048241418d", + "0xc0ee34d09069301fa5ac3378ff176efb2b526665925eaf67965699c8fa002c8b", + "0x241837a9dea17bf12b78b30255177b3f14f1871a7b9eaf5c964d30eeb2038212", + "0x2e5204320b7ff83e16ba9727b5df0eab080dd2ea374db22a664f0045540ad936", + "0xaf4e24e4430a008265a6f113c097729ba1802e23d119ae0efef2f2a8bfe4ca0a", + "0x462b2de54860e6d8efcbfde08b381d74eb8e61cd2b311cf14300853aa2f9e88f", + "0xfba00f6c3e6262fa9f282d179b976894376e7e644c1d745302cfdd21cbc3e311", + "0xfbd6361624060852c33c3199ecf0fcbd8e56549d64786d320850ef3911dc9fdf", + "0x7be237a921be1921f90265288026ab1ff7d951e1412bc93470e09bd40a17ce97", + "0x64f51dd23a43c312e57bb2c0da1fde00b48b36eb1c9142589bb66407acde869a", + "0x721c44f4450bc4d92330caf7faf277faa1f10ee8f4614f9f903b27e8b82d8e9e", + "0x71a65acf3a07914cfa45698674a9fb1a35618e6f849aac0be337e44afa7b814c", + "0x620969bddd7756c5d3990528b1ac4dbc81becbac02843b3c4c75ee991ebbf4f5", + "0xef0f2a4fca1191d5615ac096dbae87ecd3aa1a83c46c43a3cd48395a47b2b65e", + "0x5f9bdefe969c0554757a1bf17d990451a58cd2ac60cdb521679081699ba4c2df", + "0x8ffa4537562335a6e21036f6c3af75614887e1c0e54bdd719f53c4675e4e8948", + "0x700d67449ab43081029dd61cec4b31cad71335d822df7ae4053e5040e47090ed", + "0x22a9ede76ca8f831125f7e6d1600aa373934f0cefa888e84a0481247b2c98d2c", + "0xb5b7c6c9ca26d8981191b27dee2aad72dfcd7e6d135d09c3b0b5bbc6e9d64849", + "0xa24232b8ea1e1ef8a07cce001d37d148a07ecbf0792690470c106804476e61fc", + "0x254935114b97e601fc5653001b01faa3f0fac11653926e749cb114cdb8239874", + "0xdec3d44fc5130107d8270358b91d7e0d933e74c4bec6d97cdc9b6f77a1fbe1db", + "0xe947a36694956d7d65315377c81225aee6128c7f075c2a37db61b7b917599008", + "0x4451d920ca782f22a3dba977235c49a4e3b97d8de1fdf9cc9d819dbe7237ddc7", + "0x234caa79670cfa3aa39b37887e2cce3f88ab5472bf2c2e7063b9c9b9424b20d2", + "0x79d8c448a7c637fd4f4e545968bfb5fa59a62948bc7b9031eb4c17493efb41d2", + "0x0b8cc0552966a5ffa6567c51ca7e36f5ad1bbfb5dbf5a52759314721d4e5f463", + "0x774983d323706ba773f8058519a450ae096be9001790cb5e51d00ce1ff206b46", + "0x0dab876db72ff5b208e61e18360e37b2fa6425a3eadc178fdcf654ca273374b0", + "0x20339939f9a89a1c38d6a2dc5ac9a505f2f899a158966cb18775831b045baee4", + "0xa42e2405b8a4a4da00dbe1d2e2a7244e2270f9ce16f054128b93b076af58cb08", + "0x149f8b8bcc176aed12a041b55d040b306ea7d34cc84d5eae4dee06d7a1f204a9", + "0x9b8a117cd1948845b4b98086d80c3ba055a138e471fd3ce38173e3b06b55499f", + "0x22db704c6cd8a8efb8ba974ec4d0ddcf64b1396aabd57cfc271e47ddc5c66363", + "0x25366f36babf4dcd6a41a218cfc8007521c9934f3b70a6835eb5490ce5c32937", + "0x5d21555e54954b075214c3eea858256b109667ebba80b117eaf08a11d4c9a9e7", + "0xcd876855021bdd0ccfc05ae507c711b9ef1ddd7aeebb7e2d330d1205528fc2f3", + "0x09355ca9b36a3aeac9c496af56af3719df2e1e052a878731061720e1b77ff35d", + "0x488d20b385f25d7c6519042136ed3c959a7b341d0118220407034b9527bfa040", + "0x0c516cb443e22322e176465b7c2e0359823374cd4aba574d4104a70e002c1f37", + "0x586593c505b94eba3ea1f7dd3eaa1ffab88548e150997d05b336dea37cd9a971", + "0xa1ebea5af17b8c85755ca5693fafdb4fce32c5e1f6b87127b3306138612d3ca3", + "0x7cb91701609765f3df12a2c051219622cb4a662507c6a14384553364de076dd3", + "0x3d5540773e424e0a92c40204f7c531efcba2467d27d204e0f3c6e655b44e5c87", + "0xc59531ab703c789a20fdbbdd08179893bf671c0d2041631c75d4221bb865b3c4", + "0x7df727560b0f89ef9af22de693230ea551e315918b0cf268cdc07f10690ef2e1", + "0x6d95bde1d89676d0efde7c58d71f91cecf8777e701f7f02a59b9679058e67ac8", + "0x225dae1c7d795471bb2498f380f2a144bdf457734cd3090925027375303e840e", + "0xf14008b13b4db1498bb9a902f1468367e0040cf4d21922ba288adb2a4fee0a93", + "0x36f293cff22d6d738d3cee9e22772f9e3a344a5eee2caefbba321054226c4762", + "0xca2c1e34994d1c454d4abd4b2bf939534a2ceadd6c6a3103d40de4725f77e403", + "0x4c1c6a5ec4bae84cdb0f3252b9f721da9ad348ad1ca6986ffa5e5735603cb005", + "0x0d35e0d8950362606c804221ef0b5feb04b0ad62d5d90134bf1fe119bc5a9eb9", + "0xa8bdf0d3fc34cb80cd14429fbadd0a2d73273e454d96aae4448f60c42f0150ab", + "0x11cc9d0d3064d5c7468a8c0a4146df476f254b7bd8f748fb42d899ce9964ffff", + "0x6d8752e6f51856b90d4723a34d94db4aec1f8da55f2b0d94f263ef0393dc57d9", + "0x038a4fb7f9cea5e3779a026873da89b85d03ba8c9038124eeadf88fa85696f1d", + "0x9d55a956aa38c72d7fb45f856c440b012ce8969c2990c7e1fd48357daf8ecfa2", + "0x163ded59e9ba07e781e07a5329c2921babb49be1a36bc7a54e22b3bc325840f5", + "0x0afd0e16f53b05aab3a77749fd114b353fc848d5e17dd8d701c37baf5c976298", + "0x5ca8f5796743e66d24f7dd342da174ee646c60dd8bf1e7564a814ff28b0922f7", + "0x5e856feff23db5b9892430e9e493b10e2fa2db28bf6833523f158ec1946eba82", + "0x59dec8e839a2d9ba33a5a687fcb92d8b450585864660c202d7e79bbccfe49f57", + "0x8b0a8363e98ac24707164bc767c0f6df7ba6b4ed5584f14693a06b08c3a23c4b", + "0x9214f12639d8f39488579e8265d33850dffe16aee099318da700475551da4ba4", + "0xc98baaa91e6f46d6c218fdc79aeab7d46063fc22107e457eca0559d500489d8d", + "0xeed3ee9c8c71a9035585dbc7a69f91966ff45ca33d81ff0ac69dd9ecbc05df05", + "0xa4b2f4ff38e28613f5ddbccf9f8b4cd17cf50704e398ac3b9b7be043d631ac36", + "0x7de22d2449411bc1e342ad3ee83af0aab2461dfdd7dd51b915a17fbf4350a660", + "0xb644191de416e8385167f5ce348cc3fd3020b69aa7f8e4ce4b58d796b401e3aa", + "0xfa137d84153b3ddcbbea95c810592ca1d219a825a3a9144a1f60a2f15890d959", + "0xb297211d2aa65b91fdedbdc6445e56c012e7d4e99250727f0f18471a204097d9", + "0x87c07e37c34bf4fbaaa2cabcd7c72842828fe0bd4e0af3dbda59a8b7dd8f5832", + "0x60a8ed3981341120c3e6c821f09332c6b83770357dbb1bbcb535fa771ab9ce56", + "0xe79330fb9dab7ff5ebd8511290b629e0feb97ed3907199f47b422e2b62192cbe", + "0x699c36af1b18f84f1443cb7dda84e24eeba099c59b2a3d45a9757b528a459ecf", + "0x0cf4295cec116acd9b74e3ab126b020e82dc216259a40003f493f15d1c89a9d0", + "0xf89bc33459d999b54f77f66ec80613d2b4a7d7e5ca5d58d5d2d9f9f65d9947d7", + "0x2d3a04ea977f6aecbfb671f1ece353c899c6597660a945c78be66a9c3868ede2", + "0x064e090bcd4bfe32f0b9a2756a01f2764d82b7fbc71ecd6eeab2f018ca1a0237", + "0xa3d1be5a6aea2bd18f5ce206e0f8c2ca898e4d4581d2d9ef3bc1b62b86b4d6b8", + "0x3be703b1204cb9800e53bf8b1521eae921f7f6567080f9fe385e712390d92161", + "0xee5c0b22d83a67a2a9ac8661c79d8212252dcf7d4062420cb57878ab8b9dbc66", + "0xbb9813100f3153ff7472f61f97fbaf603f228eb6cd715031018b913c70070d52", + "0xe416f16bb22dba18d653ef75e374b73722723a9f426696ee7477598e1aad6458", + "0xe2d51294dba9887f33cb3009ce096a4e9d086b3bdb114fff71f3438bbd784a0a", + "0xfb6aabfa3219f4401ac3ee4bf697052aec4cba80f755208b42fffbb1b0b3b1e4", + "0xf4d28c88355ed07f3c2007b9e8b7f4e6b1ccd988ba2b32fec0733d57a178d123", + "0x824ff2ab95f74ef02726a483f38342338c16d895a1da8cee5177ee1e4719e465", + "0x8aeec0b4a40bee3ae28552df14d4b6855ffacfc3d01ccde285e66228abc0771a", + "0x5573c773bb5bb5c5236109ef3eda585d2da1c5c38eac46b908fe283c4d519145", + "0xb82014148f595f5e440b729035cbdc2490db0523b7d00f9f0fbd4c383090fd4c", + "0x3bd847f5f5514f396e663821c8865296e3d3f7e817da4e93ba1c9b98b1758812", + "0xe5bd2846840a2049c0f78fddcb4892639156ece271097b56a74fd338f3862066", + "0x916ea84d9ed21144b3f9d73b03d02fbec444449c3a1f982c30bb20c0f75e64f5", + "0x2c7c705d9ec24d6cfbfb7f2bbbc46aee8e3c81a41becf39a23ac19df32dc19a5", + "0xfb9693cb2fab44966e6a0ec4c118a68354de660ae0bbbbe4da349df91e231fdb", + "0x64503206586c60381d9cb74e5f548e417ae80767d48e15c61d2f08f50548b7dd", + "0xc7bcdb552852328cc3d511538e3a0c94dfe1b77d489f3a44cbfab30b3b781036", + "0x4d68f37bbe1a45eaa3e72e3543d23063a4eaa1d38ae56909fe11b309230c8632", + "0xc21a5df0d75ec8d06498535c40ee76a330eb90add0cb99b9f0f4671f504ab1a0", + "0x5558d71b23fd39caf7021402520090f13b50cba5edd669ecf7eac05196e03531", + "0x2164e1ba3b7d927af0ba644481273ccebc5646e2c49825d456a1e75fc7c80db8", + "0xf10702b40b03ef7ae672004cd1747705636f7432a7493021aa8c900397b0123c", + "0xdef29e2febf8fd4a3313106832f3f723469f352f614f20e7d0e6c2e298240060", + "0xe62566d6d85c3ca3d33b8daf61b518abe76b28ec26b91a0b747ec3affab41285", + "0x1c286207dfee4b99fc86818c7614804782c8921d8d8f8815cd2f190f42b992a8", + "0x3670362acbc89076aa41149033484fa2cdaae47c8c10461f6e886040cbabd1df", + "0x8a74d7452f3a76bd20e2d1686243b90243d3d0b40b5b98fd313ff33c8d932e70", + "0x13f1f59beccce0f8d8066742249156a5dc0f21918588b6b145ab40d3eb4bb822", + "0xacdff4a0adff4cfd4756dac55df04e23cc1b9a6447d11d9bd86361fcfb6534cf", + "0x54fa4bbcab952b44cd3cb6042d6ef87fd9131f1850b8de0a44f221ca20a490cc", + "0xa06748708ef2e6c391d0d11c6c8043d91f9f1abe8292d55d70d616ebc5a94ed6", + "0x64dd68699969109d334ab59d64d8d22e114382bf2ebec5fc7ec66875a795276e", + "0x6e66b02d463c76d3a217202c527f798a4c472b37b234bbe0d3d8e6c16a34719d", + "0x3773853e93233b5c39d7bd53b89b769aff1504d9ef433c46b3caca15637ffe93", + "0x817633f3cc0665d68f497362bba1d32d3512016b2490047a245175654cc3d851", + "0x41e14c9198fd548c4b70963eeb24679cd7138dc061988cdd452f2de7cb85c14b", + "0x9ef5c3f4acda962833a68cf1298198b68ad38cd3ef9f8dde0add0dce93e4925f", + "0x7336e43b284042dabf2326e15f9717399fe3d3aee9586118a6047024ce3fbaac", + "0xfd037d85e4f6d39887ea64406302acaedb64a72c1ebb122519c2fbd236d789ca", + "0xa28129819bdab4818cd82aa1bfb0d31d642f66b6f072a079bfc414cfa6d7385f", + "0x02b7d36f55c39b97e2a882d47bb150dc74bab3e3d3c44a13d0149bd87f653f82", + "0xdc6609b3e7e5acd44d8bdd69913d864061c4c5b5d34a24a25c04affed384e687", + "0x0c5adc52eb0e98c0945a821a270e00c1b8424fc88eab4e4e53d8b047c2990509", + "0xb47060a6278b3da926c799a4c01e9682b2c38b9a255427499c03ee9305156939", + "0x8cdfbe9548608021b7fbf4a9845bdc03d2187885a25972f434af7706d8e9743a", + "0xf29db31d7986d96f18766658ae38aaf4210b22bfb14ddd1ee7c8e6a534292fcf", + "0x2be2578382bf5a26aced019adf5e1be8eaada7226996ad0968b41e0211950064", + "0x0fdcf225eb3c840d6a9dab17976b86c02a3a2ad6c457d8ae90a90120cb71239a", + "0x04412b8786da3f17b29aefcd1de44359ca334d551e4b84ab93ef562c16c34bfe", + "0x0be08908e549fb47570db3f9850a43dc3bea22fb569e7e56763029c2612f28dc", + "0x43312d35db155b984f27756d5f74c5028a9d182c346287b6d22318d6d09fa287", + "0xd99d026f8d05e8301728fe546ac68d0b7161d4c5f39d321d81c2d84af8dd9479", + "0x2aa222445dc7ce013979a51aeccbb0331d0b9618bd44d6eeb41978402d787a4e", + "0x7106550433791e214c8d1bc44a6bb74c13cb04ef44d888e4e86618a7699a6c0d", + "0x4cafa83063379aa62aec4ab93ea53b6ee3340b567bd8bea89b1f576ab2b32def", + "0xaa19fc5e04747c49682a08a04e26588ff2097586546c7e6307ebae3faff31ccc", + "0xc95bd63ec24007b5bfb3f3b62b358e32b7c4b562a3904d858b30131ae5146df5", + "0x33aca1207ba456f03f62dc351c975ee927b48d2c9ba394a56e955ab1b2cda20d", + "0xa3d6317111e98d624e5a68256f142ad3c99c3d2fe997650fd65d584579f9d6dc", + "0xfa3d58b3b49da8411398ed8b288f70757be569475c02f9ba23a4d913679b8ae1", + "0xeb470c4d3513dc24664d00944e9fc6da9fbdaacad66900accf0467ecff11d6e4", + "0x9a339d9d68706dbb4300222a5ac8b5823ac55af237273e0aad0999aeab8c72b4", + "0x0b67fe0a96dd8688b8fa98ce128e025402173dc49e0de904c390ddb828f059ee", + "0x634ac8f82795f1cd08f8cafdf00f869100f84350157e12d5a87de7d013a856cd", + "0x1530833bee82f5fa0aa8dab062da230024ed503e352de3956831a92cdbe36199", + "0x69b26efea6cca6ac3ae1bba540db8fb9288e421163bcc50172669053e7751584", + "0x8a4fa2c094135582d17b7733f74e7fe4dbdd0ccd0e5671696739639b64752c5a", + "0xfbff6abda57f9b1689cb7ea8809555db3efd4b21b0b2b1351199654d044fcca4", + "0x16425718846535b46733b84761e16648fa7eabae0e5995108a2fc6c3f324ddf4", + "0xc72eb88c5161469517e7980f86ed414b4e5e54dcfa734e2255908100795078ee", + "0x91e0e6cf1fabea993097496cf7ccfb023e798363bdf8a85bb5e73866f1442ab4", + "0x2530aacd1b1238514aba9ac1d13de20aad119ac4994984bfa04518a31051b973", + "0x481970f2e7e34a0064d136e87cd59fbf6131341027ab27458b9803aeb7e79983", + "0x99beb276484210f5b55e2b3662304a73c86a46633f3ff5aedb133217b7b593b7", + "0x0541564d394f368aa15e88097bb26d951d099e103b5866a3186455f422af1b8f", + "0xdab00c8928bddd0b3773af6dda612de649c18e4029c54eaf8467f82b153af878", + "0x343a7e702237917aa5d06778a592d1f279eac470beb8f340ab338c41a31826e6", + "0xce9fb58528596c0da68cfcd52d4556c41d7d6e8b858d665bfd110bb95a76b3cf", + "0xd40e49ffc0eec56394bb73f469b0aa1e1667eb4f912f5c3e82cf395545af07b5", + "0xcccf0af25ebe7d4e84acedcc9a88e885a4b99ab77b9881c524bf0e7aab724192", + "0x0ff189bfed3b2f56a3448bc08ac2dc6eb1d64125c95971ea0ce0fd5b7ea74416", + "0x60d25468039d7e8a89117eb468b4f561ea072554520d7010047680c1ddf5e70c", + "0xcd7a3c31ab25c421547b379a508fa0cf6d86cb9ce96377a5174ddcc315fee2ee", + "0xf681f339b4698b7f5016e9c7cb9a516d230a3f769b0da08096372bc87664ce5b", + "0x93dfe64853f84944856a2b79789ac7c73d18826e9a4fa6d970dbef77d000f7c3", + "0xc599ae73c9dee91ddef71fe0303080403753e3610ab73f242f3f8fe49b598b4c", + "0xf87d38124c8014802c3f523d56eb08ddffe645ed5d84ec1f11d025a88c594cdb", + "0x556e5962355580a0c44e5d80b28abcbf5a55feb5c81bd7b7a74c4aa664a7bf6b", + "0xdbb37e17df80f67cb8958590469d6d8a602b11b4dd28c8bdf8cbe873a386e644", + "0xb4932582d58d113231ead5a2d31eb8ccc0aca98a8dbdd9a450bf66fb4d65e18c", + "0x91ef1100276650f3dcdf96395859c123ee6fcb4686eecbb97bbbd6ed6ec6a656", + "0xfd5d42dfcb6c00aed36fb6f5a38aea10198758e59fdc4ca80b6baef694793fd6", + "0xb9d56c16bc71828af33e2a58b504ad96c96ea445c4fa56ba3703d6c8a971d9b0", + "0xa6ea2c5703efc858f9ec4a96062d32cfa23feb7d7bce6c25a12535c23b395809", + "0x72c1fc542e61af3181e988774989c782875d3b978df6e837b3a601839225dbdd", + "0xf332d4922e029f84d9aa2f49f910191df88f7f1d96d59ffe03d8956b0a5d54ff", + "0xe45ca95879c07250f3a317bb34d7f5bd0188fc947bbeb04262afd9c78039e197", + "0xc8c33079ba577066cb8e5af8c0682ff5575aa64cb30e400e5ed73fc749933d9f", + "0xc3e6fa3de28bee05a531d588d4895e63452eca2a188385727d466b87462c54c0", + "0x6740e11063142bf755d0bef9c254673af1f72807c3be4c531212efff63636f6e", + "0xce52344424c05c60bc7130b05febd30f611a081fd287757442687ef4adda6948", + "0x99491636ce876cd89f40d38ead2ba1579a050c16400052d516dad0a89b092069", + "0xbf6cd1f7f026023683c23d9f0d891e765823885b211420b9a6b33947138776c9", + "0x7b478202b808e4a54037acc93daa1136c6fb27d89b9f897b9fd3fe9afba73ee8", + "0x2cb32599960b97177a94c84ad7a047967eb2e212415afb7f1626a75a75cf1b46", + "0x62ef86c77b59fc2261a10ff39d74aceb1b7f7051758e5372081bbd911f56ccb6", + "0xcdd56a98c6b30e17a3239158ff7d91a415a28e7bb7e9528cc9996c8db8745667", + "0x8e432c62c747d730b197d6462d9369e5e9c3fadb7f36702c4c76803c5dccaee3", + "0x9eca51d69547f45fac4b49f0c6f126e9b5c67259880d28112dab89ab8264ed9f", + "0xdfcf59e4a92c389ed131b40b048abcd0725aab626f4fdd3575a37b4a3a206549", + "0x5152046f56a54c8f926ed10300d516f30a4a624aa7fbb888e465ba7359c4f3cf", + "0x54135b5e02b568eeeae74e59f1796650424233966758fcd1d007a727145509bd", + "0x9253844423f0fb792a2248fb887ffd10be43114b810b2635486ca0f6d43bdbf5", + "0xf3394a79eb65ff0184fad992adf3d0a86b5e6df85b629abed224a85460261db3", + "0xa4a1b651f300133e86986fd3a881e6577c6fcac410bc58c63f4b031e2ee2bdbd", + "0x5e074eec6c53dc49c55b50c584b0855e8c5a3c64dc72e73e5cdc1b086d07596f", + "0xf110a1c5c23d3bb9ac48d2bb03972e11e04b52da5eb62db65a4f4a2ac8d2c068", + "0xad11be682bcc25d30ac1eb6ad15990499d488c402ac6ef78085ef8faa5d4a248", + "0x0e13e09b2e3b8de9e5fc9e93382f06b64f03f98c3e26a5b3af811d32136ed0d5", + "0x494944f0a5afa4efbd967941129275dd6046535ad489443db1a384798cd7a02b", + "0x38474b45706c7cc93dd2ce11c41ac7f12f494c38f3a00c9cd38e44bc8771e04d", + "0x9a986ee21db81a38be8d1ed319f2d2558eb22de3899942fdb6395a188320de30", + "0x13ba368a9882ee28efcdfe67f4bfbb263fc8bc1c07b6f1369d3bd5ffdcbb3a01", + "0x451ff9f8e52bedb6a0f47ca9093b83b7d9a61e94329403d8eb913868945e0b6e", + "0xa04bf418215767d2aa8e6a8c6bf6563c80fd6f439d8d8759d2a43d8f4fa69630", + "0x551df5f968cdd4d0b78c1927b20383ddb6183a62f80480983d23f734be98c265", + "0x0df43d41084f9aaead6b9d77cc8f7760772ea1d27bc574bcde7061985e2891eb", + "0xcd8fb777f3d81581c5ad765b4f6d7f9c8d45c92466c65fdfb02637f226aca9f8", + "0xe4a92928a67ce00b8569eebd95c446ab82ba5beefdf467ffb0ac1bb08f4f471e", + "0x3ae0b6e5ed51401ac85ed96a84326cc1e894fb89a24269c9f3cd25ca157cc698", + "0x8363e4315a1a914a3d98eaadc9d985324073cc04e55b5859264c997d6d22834a", + "0x1e77238479593a1ab25b61990f3876869d2d87c9817e061dd82eb43e6404e0e9", + "0xbf35e78bd78e97326d6cc6c87f9f9c89362234327d06a65344cc9e692cd0d739", + "0xdbfa8ae2f7df4c5c0391e50c5cf9e144b45763a13ee250033d133c08b33f8029", + "0x828df5dd874a8e47e198ef4751dfec528c34348378574baeb2cb3b85f2376dfa", + "0x60deecf89aa73bf16f800255387ce9b9b74e02820b335cd96eef4b41939461c8", + "0x4e6a6312ff2847e8e419be96cbaf266ffa7aa91139d9d59da4b29c7e28aa813f", + "0x6c0d32f28bd65f020ac3c803fbed871422d6c6f02cab380056721825cb05cc22", + "0x7639acfede0e940dcea1965352cacf2b08f8ba0cd4254608a1033e2c80b1f12b", + "0x235155d27105a7f73b46d0c4a91c5a4978eda6d69db1af58d13b89076ebb98d8", + "0xcd1cb877b7b138f598658a39d1e135ae211a2169b2afefcd80923f244630233c", + "0xe9744fc928cf00f5ce170b81d28f5bc8f324d37fa535f2cbc2a23d82406d0423", + "0xe376bdbf2b052cc9d5f00f36b0af4d360174ae54b52620439c25d3430f405cb5", + "0x5a00ca653123aa856545902da24e3066386d1df00be733f95a4d105bb722b085", + "0x8e7995454fa8c9e7da6f19d9860cd0ddcaf1232033d43325b211c39023ca4d7b", + "0x8047e3cf7584e57729cc92c406f2c9caf41d9f5b3fc3a1f14e9357c954972474", + "0x7de15b023b1f923008d08ef6312fe8c3db5f499a26fcb3594795f47a30d28c85", + "0x7924df166e716bd934f98797b4a7f344693bacef336bad132ce068c6b70e5ab8", + "0x9ba0b15b18f2735800255de7b9e52fa971f2ec7bdc7de455c2e989abd27d35c3", + "0x9a5707a0ee4ff0d88fd8f9b5382ffddc558dc6c21ed9c8daad043a4ff4a561e1", + "0x898ea840f2e1e07c23fb9e18045e8addf5291661a72d6517b029620b52b0eea2", + "0x8f5ff6dfcc7c8ea2022c7b5d8ebcdbf713d303f164230a74192ca06fcd2ef20b", + "0x1508febeb95c2d3ffc8f1d13787bb9883be3529f8d71accdd55e747211fa8c08", + "0x2c7e4797847fe62a55f64c64977462cb792d9ec899e16f1a46851aa670d09eb3", + "0x825588a04dc941ba395deb99c00c282373e042a064cfc1ff557f54d8e0479187", + "0xef56430f5950a91b9a2028a549d4aedefd7322e36793d3cfaad18f97b58e9e3a", + "0xb92537e03ab350a4fd855bbe5175e8ff0e0df0fa8588885fdfd33bd2e37650b8", + "0x57b54ecd05c5582b98b62caadaef9e3386962a15c302caadde0f3ab2de98ab8b", + "0xa3f9ebe4db242dbfe248bdf7f504fe1f92079c60babdfad99a1a1adf446b3750", + "0x0939ffdb6c07b240cc1448f7276d7c8b436051e9488942d4b1a5afe5a0103aa6", + "0x778210367f625571f9d075ba06aa1ef5a8b59a411ec25b4e9540e7dede11e013", + "0x12087182676e11950138448ec4258ba3bd6ae31662f13db6cbc46c52a266dec9", + "0x7dd65f0f816152a53d8221e5554f4d3dd293219b0537a600caa6c5b758deb4cf", + "0x060370c4afa823b9cac3b2a87af1a989f8478169da3e12ad4073731a6729112c", + "0xe57c5b36d7372b192549e2ed987cc16beb412db8e22c3acdaf1f1c95ce1af81b", + "0xa9946d302b6f3dc7ccae4e4a69c21c862da4dcb9df371a8b6486534d8d3c6e76", + "0x350669a215b44b8cabfb6019d89f5268b299d6fd9db56fbea93cdd9f4991a977", + "0x4bbfa211e42c2b92c1cc979f6303e2eb428292500c6b59f0ddeb87c3fdb4b314", + "0xd243e1fe99ff82487def980022b204794fe4eb5b8fd657b851f7f5f323eb81f6", + "0x0eba7b7031613d9c2cce87ae3a9ff6e7b57b8e537913657c5c97f80a65f774c8", + "0xa5ad33c7f0755f82a0d6f47ed774bd8f2a00a66f2a967a52372dcee62f52c099", + "0x36d42846ce8b3021f00a615c0423d1bb1f00ed05df1748249a79c8a515a5cff7", + "0x7808d7655fb173387cc5601c469a26402b631c1d3e490b1c1d25e2e2e4dc3807", + "0x533b133383362a66e34e9e261442d4b867ea762b4422d2ebae579c144621ce20", + "0x46e2dffa796139a441b4b2a2ecc9530924ba032bee783cfae1ea1623508d55a7", + "0x0acaaa36ef65ec28c8a5447a2d594e48e4382737fac248b7895d3521cf2730b7", + "0xf191dcf174b7a1b026a9e2ad51a49a684abbd84556988b8cef315c3c5804f032", + "0x940284119cf5d5667d45103ef0cc790cb6540926c0f645911c669ccb78f6f127", + "0xc8be6193a1a094924999b527d81561f1ab47966c6fee7699418aeff2e16dd27f", + "0x3aa137a876b08b74d0e507ec47e72ae5d2049e01a83c68b90860ed7e9b594f4c", + "0xe375dba7c6b94fef6314cf569f887010165f40f930156e15c2897ac50db96e4a", + "0x82c766f09a89d2d8644603c21005c342208e2a61922eb13b476dcfa9de9ebc4f", + "0x06bb8f251decb2ec72365235d98bb9c0e0d0a18f7ef7b069f70eb432455ebb7c", + "0xdc4d5d8aa9f4a4e224510a32df3ac984428b8bbf38a9638fad1c74a22cb32c0f", + "0xfa394e11c7ea81618a3735ff52c67d182a013caa52897b10e1057de7a95b6f92", + "0xbe3dfee256c7cea8b110e9915e167d56479f5593cc5a8bd7e6375539c9ca8984", + "0x3caf7941bd973212ba085982ea0707889aea831d3df45468060a1f8afff8da93", + "0x17ac6c1fa00755480ab3fdd4d5969e8477d55ecac99ad1b1dc123e677560062b", + "0xdaa4e5841582d605767a49d59dcdde3067a61681f40e55e19adf73fcabdc65a6", + "0x465b0a0f2c6dc1583d1517a303947d372af513284cf2fbbda30831032c45674b", + "0x450b14d7773bb9ce708f8dc12aa7b8a9a83161eb8086d6fbe91fdf940f24cea3", + "0x0893e90abc7481e9413d33560ee9087d5e39305c0ee7fd8168d09da19c9a9e26", + "0x3b3e3615b3cb2402f807b1f7568174a6972421f3c6fada50c489428ac6d3a3e0", + "0x8ca9eb6c550474b93362bc8eaf6cfd7f39808dc115242310ff4ff6e5acfc121a", + "0xc55d95d03f1359410a23debadc8691ea0b53f4cc50073b449546c4422b16c037", + "0x26adc021bed5d143a3933824ecea25e0b6c577d8cdb9b899a86fbf706338dbd5", + "0x81b350f0026673f871a8981d899a31e2892730fb58e4b0273351b256e74151df", + "0x8a97e6e927342a4b9ebfeda5f5eede61ddd23b8b4421a0d2a8baedfaca168cfc", + "0x96ac659b574a4e63a8874a91fbf045f90f460b6c5b1222d72d8ddc240c870907", + "0x77c0baada7e37994e9f91075fc8ec335ce0a5d4f1627ceeb0a79034bed0b0f41", + "0x3b626e112b641b7ac01efabb2faa0b0a9166be0f156becd1254f0365d6f08a12", + "0xa15072f15329fb0c090eb5f1f91299421abd81efb05a00efd1361fa10228d584", + "0xb44bd4b0648b95e0d427cc5173d605fe21b00a5ee2c26d44d7c9a4f49e0fb521", + "0x0794d131e1ad17eb35301fab7566269e99b48b2e4cddc5b75f0aa15996220f03", + "0x994a7c6159433a62ebae86bb4212983478372b94fb3712bf025fbe3c5169038f", + "0x154a2ac1a23bf367f6873a7b51014d4377dc1008767510440a490365c4c1fedc", + "0x5881a0306e88c24bff0354b5df839f86178d2ba5e54d3cb80889df8e72952c38", + "0x770ae9a90625a5424e470f484f2c97b4f191eed61e15813dd59cafcc5bd0f127", + "0x6eb956fc5c743e7a2e02d694cb9c643e5ef97f960e90b75989aa91d3da1edc5b", + "0x3100b5a08d157428c6c10271486d050f913f436fc99e6fcc9b8eb1be9dfc39fe", + "0x507a78e12cf89269bc156b2fe5d991cffcd6ca6d7d085aadf8fdbb44d7689b16", + "0x0d2d43bfc425c8686487ca97d7ae5888535f5bf4d5e4feff6769ac36e1ee0946", + "0xfe866c43ed650b6fb1d3719df09ccb5126c36add42428bb8048a6841a88b4520", + "0x73ee2d004147c22f7beb21ec84293d6cd5974289a6f4eaba670da364df657a9b", + "0xe20395be53c10d9885bb2c7cfd78927051903ff4ad8301cb6f4e54c471b79308", + "0xc039903cfa0af94172d211f2e5c2c653d7d31905a1c194aea0a4be85c2dae075", + "0x468490362f1a7781b4e08d05bc1417107daf8dbeb092f1ab1a9e2d8e604741cc", + "0x186a3a6e4e1ce30ec18c1d72ecc4fac270a7d1ba9cdca0b7937794913f25c603", + "0x2fad6bab068378729dc7edeaa8ed516b6274396f21d24382d889f1146deedf70", + "0x257f8eb447ab096ce41e774fc4874335e77f750cc0d6bf4bf3e6b9d699de203d", + "0x4ace53b3bf327fd95451be5144dd2d62d906a25234959e33410bfd28e615bf11", + "0x0dbe710b12cd27f1196c9d7f1c5a981194ffae507b36b287cb758c32e264cc17", + "0x2c6def5c3a93b3c718839efe26835b0096efea132f6870ece1c33a7e91a2ba8a", + "0x2781ca465483c0079336532daf646104b9260c29cba0b6f4399feb557760825f", + "0xfcdecd6aa3e2c1b4d9f1991894cfe5d75f198dd445cf61ccce1c32fa8ec19e98", + "0x951185273ba63162c2d920f5acc888a7e4c1c6e755ab1215d8df5089276caf43", + "0x3e092b4e7488ba0172ed20dd9189f06d19a74269231edb0a592979eaf7c0ab88", + "0xd03afe649dcaae2de18474e82cd88935f50091394511a1f334e7590249d2583b", + "0x20c54aac1af04178d8c6ad55dced6e5cd2b83963f53ca65cdcf503708206285f", + "0xd8102d6cb1ab6c9f5f13a38ad0c375587877b22a6836a882f128804d5577b527", + "0x323445a4e659b00a2abd18bbddfa87dbd824877eab5693930c63a4704f0072f8", + "0x21caf56fdbf708f486b7756928bb74f117cad771a850dabc2a69291faf2e8d6c", + "0x4227752d2a7118b37d2089ed9319095d388c9048c0082f337d62d5275abb1145", + "0x78b96472d555ffb616105b2b471e2893ad6e1f8b735c4596be9d74f028932693", + "0xb11adf3fa7f2922095d6811564831e7371f87f4d417c730927949cc6b5227bda", + "0xebc5565e3b1338fd34f4427bb60075f6461dc072b097ea9a95237a411cd8fc8f", + "0xf4becc05db259bcf2790f79cc1fd1c88396db483c2c85c127818374be47da818", + "0x8178c8a0e6eebe2be426cd30266ef6574973d200e84339196bf6401dff1fe746", + "0x3d5a6f60d5351d577ffd38306e3a12606a1a3aea9023f2b8b8bd2452847d6b9b", + "0xa80644fc78939a7e41d390c6156ddc0d2b96f80265e5ef333c49e167dc585ef9", + "0x3e141f89a256b3b45cae79ac0c604dd9a19c09af6f685157872b42031bd62e1b", + "0x01a6910b5403b23b6e0102f4e012e0a0c06dedb944d36ad4c4e2eba8cb3be4ba", + "0xea5bed79158402d5f0c295f363a211b26d9ddf4d4d04e95564cffcfadbcce518", + "0x3a9d2d41ee14a76564ccf9a56d7c6d1ce732475ffa64499706b50e8ecd0e2050", + "0x8be57936b28e208f26fd382bc1404ea474001287a2c4d21e5213bd9327253ce2", + "0x672af36bc6de92544997f7cde2ea8870ae0f09b4cb2c22e8fd2f64bd88911f60", + "0x0d8d34a4ca78aea4627d1439402d4f0c64f130043e45bd6db1a0511dd16328f1", + "0x376539fe4c0de33e48d4c308800fbcd3606b82482ef9addbf5bf9092d6a55f92", + "0x93a25650ba0615e36facc75fa2ec8e86d6135022a31d86722307432871d6b72a", + "0xe95b8505b1ab584e83efd0bfb62a0d3c45785ac2ab74ff5e58178b2e742aa6d7", + "0x6fb4db8b3eb5b55916e2b06536b179ffec93b7300e3be36234e4ebb915a840bc", + "0x5d7545abcd38bffaf08f6745872e0dec47b0867df5102f42d83fce722dfe4406", + "0xb6eb5b0ef418cd1158ce9588e003e944d8afbd704eaab53d2643defa365115c3", + "0xfcf5ae4e3d7e2ba5e29653d5b732a1d86f18e2aa79ea2354958ab5dda5c57f4b", + "0x95aaf721f3c015305eaa78a8879d2f33b9270deee9d03acce855f0f44480dad0", + "0x054b3629ed6ab0be7ad07d3c658acc3ecfba8e9ab68492838b35690556dd722c", + "0xd87e3fd945b27bc037299325752c775a76f1c3b5b7c825fb9b6e2f6dd3680975", + "0x827fb42c7548a9f034a835a54ccc598398d2a8281677638197bd8d2bba4fdd31", + "0x7db908e4263cf7cdb822e42acf0fc9aa94999a1ddc327b6535e0a33c5237d1e5", + "0xa022198a7f3027a864615df968dd6c77d03878e12b9f6adeee611deffc82f33c", + "0x19269380075fff6af87dd26b7824ea70398e3802ea3c6fb2b4c2ee630562dc75", + "0xa13f3c9a337d9aaf86b0a5e5d5bda796d2f1d99c7548278d0b752e304392c3c3", + "0x13067c9ed1f15b7365541cf00ee6d7790aa0cf24ef3c60b10f16d0b5d0d67266", + "0x8d17d62ac8abfbe2b9696f918d92912199177e94ec49628baba2ff3897ce8323", + "0xbf65bee541ac0ed5f7529637a156446214c77018c5c59f0774b410bc9d2812c3", + "0xb3980e23fd6ad0683e19db55b7a8837a14c5c809f52de2950788ddc7f6d2187c", + "0x2e633f27e48af14dbd484a42b002517456028a2323c49916d777ec5d00accd2a", + "0xa44bfae290d8ac9e0ade331b4c362f5008152a38199996bc71d17e41d4a90069", + "0x8cfa7989fe18f803b1fcd926ed6e835286acbcd4dda22ffefb24cd3fdacf2389", + "0xf5b997a3440078a7c1322b70bae558cffc1234bf459cd295d5cd3d9be135242f", + "0x2fb715d33fe3c624ed35cb931c0a5b38475c1b97b743c15d5f0835d4836c1e02", + "0x9d1f7e02c66eeb6a75899233c02467624b46b7720a0a38b9af62a363480bf8dd", + "0x0c0a8f695812e88e92db4304d43c7a12c4958350b4df66813235dd0a69fdc643", + "0xca7e816b73d005cf04f08dcb5fcba1657fb6d339a2435fd74670155d097d6fda", + "0x4160cfa822311731d67d0f23948b99abd2fab077dde4efabc3a9625686a5aa73", + "0xdf9702f40c2564b7d460c35d25642b58179e12e859f935f7b87d41bcdddb517f", + "0xd24e79820676effc95265384449ee4bd8edfc7b747f2285b99518a2658470c5f", + "0xc79e6494fb1d7f01a4e8c36b221956e16097e92da140c3d3ccf336d5d99252e4", + "0xff84f37720a37852a94d31e3552d7605e403ca42c74830cd2856055448bd60f6", + "0xef0c3c8ca70dd9227d2cac50c3c6dbc272124b74eade2f26e6d20e21de527f92", + "0x82a1e299f56312e65bb42a054c044d58914109a9e914792ae888fcd94e30897c", + "0x948894d3791476e20b63650460960af12d2a33a4d9b23b6509ebe216e77e1cc3", + "0xd7dc6732db63ff742830bd429766ae73ee21ff37fd3831f5ca0e39c3cc16e284", + "0x594af612776eaa16e4f10c2416d5912f025df7c1284653ba844bbb3985e53b1d", + "0x348ee29dea90b5e6cd96abe5aac2537734efc4f5564e854dfd8153408f6222d4", + "0x9eab3769bdda9b57cf3b2a8d07a6dc2e07c856a15a79faa7890582b43f26e6ac", + "0xb97f381205bb10d19ffa24dd62f64b8687422c1a2967ca3b1b1bd59e30f5b5b3", + "0xf2bad3504422193b7f8dec65b2edb5bae77a3ec12a6403be0cf98e6372c59886", + "0x36a4a3f86f14ddf99c0822988616018d1d569a55f3e51cceab26d2a5ed6d1f3e", + "0x2976ced02e0fd21ee9992f847e912e4cb480d676347c721a1d8cf088242d2208", + "0xc6fe83273f10de94767eb3d67bd5e7ded408d674248cd87ed64e1e8384f15998", + "0xbdbe5d93b2812f44914fcb97b0fcc61a6cce96b9bc811e3cfdd6c9ac05619faf", + "0x415e343e7ae318153d4a9930f9779aefdfed85eb31a5f63810836beee854a370", + "0x33767db7f68c7432d03ccc405554fcb662044f6e43a5e073a8dd473f012bba66", + "0x32a7cf4985194e23827f89e1589da83b9158e98219b173f7295fd0c25310fc79", + "0xa304a15516c340a220b119243565f8a75bf6edf678029e0344ed6c1a49fa96c2", + "0x88c0c9b589f7c89d38f3f83a18dc38d9a3bcd752c214805c0876ddf2a3b45b86", + "0x216643d8038ec1cb6809b8ac77cf909dd160cf433da853e0572705e00aa210f9", + "0x4e5bfb91e5929da7d07e2e4ec30ca006cfc8399439cbab8c0a612e1f951fe35e", + "0xb20afc9312798f01965cfd83deb2a254f477636a565eb0578d8b6a86abf16fe4", + "0x83ad6dabfd5de39138c7dbac41b9548ec3fd601487ef3215fd520b4d8fd505f0", + "0xe96fe232f32f9f4a45842d502ca714f933412210eda69d28eb11bfefe1f60786", + "0x838711b43402e0c6ffa32b6c96ff20d47d8e00cfa39cab1526e7a6a6ab48a239", + "0x1d29b1af9a80c63e675ce5e957a98f31c57975fd6ff48adc3f62192393881c9c", + "0xece5ad70b3b260c8c7247d1e29c95a6347ade7034c26e9a470c0469bc9a91900", + "0x2cac591b892b14911202dc80a32e50c1c80a82a6f78a8a704bbc3e0c4f726cf1", + "0x1999c48903a3a6ab132189d27889c19c29eb04e65aa7d9a8c35722dabbc66cde", + "0xd03f863788d323b508d695a6ac3fcbaf93b44401efaebd5b7942c635c10a7eb8", + "0x57e92bc43ab1a66e45c054e6b46240838422dcaed9701d65e3fe41035b7479ce", + "0x848a6f66c7f5c3c25ae9f0783891c5f004600f274967870a4e9aeb91c34f11cb", + "0x193448bef47c66cfdf060191e4bc58820a3e1c9424a8c9933a1315bbc874d4c6", + "0xb9821736ea8e93609f701ebd178f00e87d4c7931deb9287a76ee40424eb377fd", + "0x88b01f8ec4bed91861870404d1191cd0144e0615943c609e006aa7570462ca0b", + "0x4014996b1a6ec5e1bc625a304ff1bcbd310b7094065dc6a75cb4070f44fd343f", + "0x1b1db3c8dfd7843c993cc50b4dea25179310ba77d1ce0459bad775bad54c7cf8", + "0x7ac6bef10622a0863b979573f8b9b34b9008980a6752027838ba7a50e68b077b", + "0xcaf393ba83566504254674bb9e8cabec2b1c366f4e7a867b8da663fd0dee1a01", + "0x05769a695b0b171e849df667fd873d1de4d1b0fcf67588986569e631417df8ae", + "0x13c76a5de115bd5bf30c939e21504d27e10d7e0ac5ea1ba4e71cec387b469e1f", + "0xfa24eacdcb99a7d18c49e095649ca414b3477d4188a3129120847dd513083030", + "0x907c3b7af8b74131260997f69c41850437dd00ba24b660371f23a1706835336b", + "0x4e812bb9a20644e2c48296e39688409d3499bb2a250abfaf8b0cf56ad0125aab", + "0x3238205f0b9d4062ae1ad8a5191d65f2f50bb3c0b7e1351fb8b66fab97a314d4", + "0x9130f27aba4aae0e7d48375a67f25fff231998d7b7815f24a2a8e86fff39ed6f", + "0xcf32280598cd5a0579a5f250b8e418567d31f3334fe8245c0a874d508eac745f", + "0x150f4bcda2e49b6d09a975335d33dc480db17ac59a508f28f6f05dd1d3caa22e", + "0x379b96da6a733c88a0888fd3a270c889f5e2c0728272e3ed1d654c7fe582d9f2", + "0x8df473f7e356f0dabe0d6f917728732a3f9e0519cb8e399ade5dd98762d0c1fc", + "0xf8b8368aa697e7748404e589f2f2f4d2808e7cb35086dce48437702afb8b882b", + "0x2b3560074c07e7955151dfc09b1293fd759d286b65dcdcf7b2c9f3dbff404816", + "0xa6361ee7b8af32325cb36304f400c9c685767c96d0b5181e7503ddc8a36eafb2", + "0x51405ec92bb2c7fb57f95c555150afb6351035e7553a63d26fb87e1827a6c3b7", + "0xa0290cadd15d77123903184806de7f04723cbe2eb4bcc2da4c926bfb3195cd54", + "0x4bbfd31db415a8bcba7188529ccf09d7cc10037f2c3dc7671ace6d4dd60a35c1", + "0x918152123a239ae6c50d9895940626e7f77ea624596d1144989a5fb7e10ef48e", + "0xef73abb8745093394fc130c7145e2de31c9457151ed78bcf63171b7633e22dec", + "0x09f80e170b2a1d599185c562344821d0e36bb48f91d8205653eba143deea6cb6", + "0x793055e11ded973ae3b55bb95ef6e985ecef03497e76e4e23ac477f6ad070fc2", + "0x2ae7e9c72203cc2aacbb79cd7cb5cb7fc4b997c9c740578253cea957ffd847e0", + "0x9f8caf269df06cc8c81cde32f1ee4f081d8a2ea8d945663e0339456e583c4703", + "0xb8533ef2fdac7587034839d1ce877bbccee50c473d4546c3fceeb7ecbf661f77", + "0x31a51d443ca2a588596b3c6a0ddd5fddf7abe8138177babd8205d165de21e76a", + "0x0a60c5ce88246c2e999725b2be6c330dd2482145fb0859ac0dad38f11e24e5f9", + "0x87d81ca8b664f33920634c62eab8f0ba3fafdf28ca19279b4d0c60a71eb738cd", + "0x1070087084fc2cc2e47fe731b8f08736cbef46194d16f418c87befc693c8a8b4", + "0x09140a959753b2c1622f0ad55f8f5d34b5f52a5ad6de7b6112e4ea1b8fda9e1a", + "0xf3af8d94fba3f4a6e018ebc0fe5088710876209a39e37b85e6b6bfda70f097ca", + "0x3346983bc9ed1f7f4f112b38526a92e7b416f30e4d1c7a934c7d74974f86dfec", + "0x587472df09a53693596d6bd1f60485df25fdd54fdcfa4c20f3558107f3bd4588", + "0x4c408ab0922f27796fd4fef68bc20c6385647a8ad4397a4a553fb68c5ce1710d", + "0x2eddf19c4a6ea3c2ec855e5b90ef78f3f861bc6317369db137f1a7f89110e69e", + "0x30391283286a30ea6df7b5739c7955a1a659bb0c46f3ffef8a58c0140d7386c2", + "0x8ca8497844280911bd97ce06b69d9e09a86ea135ca0adce596614fb6c584d0e0", + "0x9a699d399b3ea784ecc1e4f2dedd9ebf9a383fe44f40b4cc67dcce4ce98cd61a", + "0x013681943970f17a86dacea448bdbcd0d0ff56b243ef2c147ba77e58f1b47c8a", + "0x8717f032bc745c9a16eeacd20f3d86872e9cef3c4c777a8dfd867ab898e73fee", + "0xe3d4059812ed8f3d4e23583544131dde2a56276dd513d2679be07a36ee08dc48", + "0x265ff09a3d0413bc7bef45d66e34180c432b5f66ca96f8c39d390a83c5821411", + "0xddcdf72b890fafd78daf04a9e2afa824de34b761c25876763d9814fe41a9fba0", + "0x75168bbb488d49dc440360206d5b36d8934b1d9c50d8d8239cbdcbd27f238796", + "0x57aeafa912ef24841935fbe295c7f922b61bd729afce4537ed4985d06fed8eb7", + "0x7be35f0ba773bbabb596b674d73d8f0b9e6746688322dc99536e52d6f0100183", + "0xceb32481ad49ec6c259273d2b302352f121fe3c0d6a38c2576fd0667ef1c1088", + "0x1e1a7e3edefa2943844e452e8d4959f06681449e91622a537e196c7f3c069f5f", + "0xc4c5f628c0e705d6ed6e84b26f74d76a4ae13b9a602f4ca8fec8d94318b1e05c", + "0x324a02a05df574b12be04d2ae137c409a87963d524eac913d2b671d950aecfed", + "0xb1c115671fde450792a1bf3541d8024e84fa4239b3fd894af3ffa25bf4d10da3", + "0x9e9db6ae30894084f36122ca1946b96851c0e2282cc4361fdcaec5381552c26d", + "0x230b1fdb77d049d548a6cbb9b4dc8ddb983c3225591a323820b908cb79ac5ffa", + "0x88bc17b5d10e4b474c9f85c935ae3cd914aa437185f9bc7c03d66cf407e102ff", + "0x6ee030f383a99122c498a9c43bb51ed46932be9cab7efad96fac65f3b39d032d", + "0xd600f1dfab24e5b671a68e56d2d27f288e9b15ecd870b0fb3d811c1cb6fe17ae", + "0xe892c488faa1e15b040acfee5411b0c6cc93d1d9082a605436a0757a0cec7a85", + "0x8607ed1eca84dcec9c9c2d9ca8f9dcc952de7ea3c54c051d0d41e78fe725cebf", + "0xd9f378c67ea3834d89a85d72bfab542d6877370c8b542d1be2766e867a25229e", + "0x725ca420d55211930c77e292f602f58c281c12a2d10d77ac70f128c122272485", + "0xc141d38375f3269e92d6ed85dde2cfff491b9fa60b3554ca9909b944838f7781", + "0x572f4aa12ec73e99f35c523c864ccc964422946a0ffc5606792cb65944a1c61c", + "0xfb2f827d05901a25debd15a6b5a45d6a42f2fa84e212f79493e46e760cf8d620", + "0x9f08663b613bf973e1861e244e61e4b842853b8a388360234c85982e94ad96e0", + "0x17ce1320123e19c42bf806a0f204c9475cb3340a0434eddf3d8954b08d8a3c26", + "0xb3d935cccef673064e94e186c4649d19dbd7d6fba4ab7802eeb44fe4e6448bb2", + "0x2a04d4c2b009e424a88196f109923ab308052c6709b0f37f31131a74db07ca2f", + "0xd30500ba7ad6548049313227f37e094507e03d2a71792fe28f2dabe6c7a477fd", + "0x133f0204075c4a44764bd55409878a85f730f412a64d8098bf26246ed89fb88d", + "0x0dd3f70cb92468b09475aa687e81d84b7b5551038813b1dcecf9ad62394bf29d", + "0xe9b2107b3efdfa5b52ebd9d25bc17cbf934190af323cbb821aa01454809cc5b7", + "0x5a10e855a18fb6e2beed325f9d4df38645e52c8098761996a455eb3320c2819c", + "0x7ff65f7a24f95a7408272ee4122f202e2d651c084495b5173bb046bc4618f96b", + "0x06b0e4bd3381c9983715fcfa8c3f48b4beb289754fe7bfe8caf5eb21901c8ad9", + "0x5ef4e799c3d7bc81fddaf790f00166b63255836f5e03bdf584ad2535e05d3cc1", + "0x9efde50127e985d2c255ef17a7a5c82b46242f18230cc68bac49867184e897e9", + "0x78309317ac6418cbf58090dc8091b3746ae1b5f05b8b082610eb28d101e519b8", + "0x81851a87801abc649ddf08e1e372e7d0a50c8d18661bb3a13193389b06854ca6", + "0xc5170992c381aff40d4d02f0b1978ee689dc9ea00d0665d53c642c6e1617f82d", + "0x9c4d06f36bfd2a08a02715c5eea44ca07adc04b1176f7a47a3404af181feefcb", + "0x7f1910a654df3160f91affee5f286299958a5665ed0d37e39ebf429b281f5b4e", + "0xb60950ce3005d7854f9f957b4f1f49f0b4e781728697ad312769ee4ab80433eb", + "0xda6666bca71087804de7b3567e1f0047bd69f8f62d0bc7f63aec65ce8222ca2d", + "0x7e9625d6fe07985ae5a378a35cd5108c1db05e429328f639504fc17527b2e04a", + "0xeb606494a39941d272aaa9cc3e18d2c502b0438de5ae974e6687e2c2c433104b", + "0xfbbdb8353f8288217930f085c51e0c67eb9fa268cc66c0a96673918501119802", + "0x071dde3764caad8163d85a5a7d9a05f31b857828db55819afc95b08e10d6c87b", + "0x262a03e39027887a11b93963c7ca4676cc2352fbe46a05ca2e432902ca4235ee", + "0x1366a718f5e2a2b328943afa839241903a4bdb776b6f732d6a0172ff8c9f544c", + "0xe04d325284bd27091857f289c6bc8ded91dd16ff7ecb157e39965c3c98058e34", + "0x648d5113a9c413874f2e8187905045f9d8e044e29cd4dca0658d283d0944a130", + "0xe7706395d101ed29949e59b10f69214eeed0377be7af3cfa8231b917c6793023", + "0x7d37f50db8c210bec2d202a7211565d53ccac0e9b17baf887a9b865f4a57ddb6", + "0x73302fd966c3e65c0d0e7d610dcfd1a87970d26fd2ac15deb73d812a878fb8d6", + "0x7cb0a793aa40ea9894377e0f941b284a792065a8fb20a0bf60e8dc51744f07cb", + "0x1c2435e5e922dbd482e3279cbf88a632e34fdfc1e7bbadba3247e11089871ef4", + "0x8fbf585e305c90965c47c190b2e125276a5cf5747ea57e60e9bd36feefc7225e", + "0x320d5616868006a5f311c79c86761b47f683ab7ea1025e794a9ee45b699793a2", + "0x8706208d64c31ebf34d6c4126f72787f3d604dc7c0f72ea74075bd21afe21686", + "0xac6a123d378afdcc284d34bf487714e4cf9a090da9d9f3d67bd5510ebde00415", + "0x623376328e2e3c6ffb9a25250a24a5b8486915317fab74c46c06cbda943dd74d", + "0xbbb4c4513c3902167bc14cb17738633113cfe22ed5846d409fde2446740f6b9a", + "0x62ce10523cbfd602cf3e3e5b8c09b310f4bf1d7c645e3a2cca9d20d156b51f4c", + "0xf427a9a8c4390d7a4237f0b72cd19343c4986e771b180421c21dfb37e6912c74", + "0x52980fbe4416be30e88b1fa6e3325a0037feec3b6c40ae66f9d5e884f2deb153", + "0x9aaf58f469e9c8362e3016de4b12350e8f3c28f86ce58702fda69be65267dd04", + "0x0fdf4d58461e0e9477d39c18eb528501eeaa066dd61aed414a50613182057934", + "0x708b1667152c18475d00a25c8b87de03dfa6b2270bda21278c0dc0faf21464cd", + "0xf7903da3c49bd894edc260ac372926d90763b459539eca32eb3865d4a16c9bd5", + "0xf9b6180481cb5b2f4692793de59cd707c71b3f6f7396a40e506fffecd3c4fe7d", + "0x6f298c8d88b0e5120805b453e0ad10f6d65809fb0558c4bb5a344ee00aab4745", + "0x4bb3b6e0cf86c6937668b4a298da229492d98d99212c88e3e60af9e89fe7a4b1", + "0x0e2b0211c17ff6e28c459ea410d3bf33838eeba869c4d46238a25994fcb9dfbf", + "0x113e8f5c4c21ba5909963c3e65b112c78c19b8f3d6526c55de1af9a4b1eb8d60", + "0xf19f2f2e0a8de0d04a26dd1a10e96257f4a6a764b92d7c360453db32d65e89ea", + "0xb20efac16095e63f5f530de22271ecebb1fa7bf4d8ce6f49057327d692caa5cc", + "0x8781f616535ce6b3352820758ea0ddc74ac97e49a3ae9eae6ab0bda4e8741606", + "0x205895b72b02e22c73c3fc2e814c121d6b899c821d72f1b02bfd8c122fd817dd", + "0xee8e1b3a255140333295253ecfbf9a2d375d5ba1ea2879bd72088f6c62b3f9a9", + "0x4d3c138f077007962fa3fc56c7e5bb409c860d11ba48ca9658e67473fc4c4417", + "0x283ee3738c2bda7631f3f30fa2c29d3f3bed26c63180c61e1de03504b48f7c38", + "0x900a45ce2bc0f4b331d3df872f3d34c350f6b7dcdd704e36670eba878776b303", + "0xcd148e1b7d3a4ed41c51129d14cb226ac20ff975de1de2a802ba93b0e872f2f6", + "0xeccc9ff6f75fc844cdc8a723ee3f168d3f54788aa99a5f81aa9e609f8b4e4ba4", + "0x9e4ba2027ba44f31bdeb8ef27db30474de2f3728fa0607f098eaf8f03faa2fee", + "0x8d7289beac6c06009f0b72c2634c176a0784afc8bbd484bcd8040500b15f902e", + "0x9624bce0632d9a49d9b040a45da65e1d686dc6e6eed7eb3ec1536384b5cb3530", + "0x8187e9b62c764ebb82f5bda0bde0cda2842c47dff910e81c03db5c83dcf02da2", + "0xab7be6424ecf0041a1a0aa6c57cc231270ee6e50b80e6a8e07f6e40e8e11931c", + "0x43d8ffb420ac0cfec24a8d378d7d35230aef8887268414779b09247543b2f044", + "0x185cd7124a200639a0f60f1412ec44083f742960e46897a85d45a04bf5ab2516", + "0x91025f84bb714cd201f4d3539f7c5e6c7cf9fa83b399704ed0130b311c6886ba", + "0x13100633274ce0d423732cbc1bc1610123959f15974868af0b1900b74ca4cda5", + "0x3b00927007417f0d762f6b37c7ec0441e963a9afcf3d54e5be1adfa4278010b7", + "0xa8e59f21955c983044955aca7bca29aad4d408a1c958ddbc2ae526a87e3fabbd", + "0xcb836def3579f067bc45c0dee5f9a80eba544348c7bda65964c6b1fa2866e0b2", + "0x404d40e1bcf36d7d4e34b32eadbcf5cfa858be1499b088fe8ace025a2e73d3a4", + "0x2d9a5f05bec8712d206349bdc8fb8ec897cf2ed28d295cd275c31c5ce46c4fea", + "0x48c577fcf9d050f2b35ba0195606d8b8d91ac17e524fe0b6ec16be1ce7cda0d0", + "0xcffe1c3ab5caf25224c870cdfd63b4faa738362299cd37cea8b66cb1542f0201", + "0x7bc007a452ddb66827cef27c5bad01e87265cb38cf8e111ee7eebb21cd263bb8", + "0x8c26cc0839859eba7744b73a03f6318e3ea249736ae20bd206461e79bd7e401d", + "0x165e4a10b343dc952ee54c56f21b74b6486929dbb59ff24e066115bf35c223b1", + "0x2ea42214e74cd75bea948323313453dc6242fb0593f7e267abf5e544e0ad7d2f", + "0x2caa11a0a255bb4db80e305ccff40608534e638a17cb8bbfe6580a035cbc8ffa", + "0x0ad0490a3008883d02cc92624352fb7caba9a97cc46f82d1e64480559778edd9", + "0xfb24953b551a2b544ed6c6cad2755e6aa634112c3e225607a0efcb0d811ffaa9", + "0x4ec8e3591d27b911b1495f6fcf5e6dcc9461e9e602b67a5bd536bb0e6bc68f88", + "0x790b7eecb797e391d27085b27cdc5d4be20bf78de9918c3d1ed72ece2738dfee", + "0x7b73acfbce9837bbc5f821412c92a1b58b3af2bdf08b4c4cbbd12e151efcc90a", + "0x16e576b05c5c95fd38ac7d53699ee8b8b3431d78a5f671535590cc1c59c78039", + "0x2b4bddc77ed675df91af490a565c6ece5247335c90de744d6863e24e624bacbf", + "0x277efde1689605ebdaaaf4b9364449345ac664b5fe270736b2a7f2a97301dbbf", + "0xf727799af7a95e92eaea1bb2e3a0f79cecea663113e4781797101acfde46c234", + "0x54eb62372f7c5893d6be0a031edd80a25cbea3db2a0edde69ef4bcbfeaa24b4d", + "0xcab1e0eaa0aa475de501b2e3fbb5ed790744040a465a4256c4397e85a31f9eca", + "0xa0c54864bb507baf47aeef61433ef7418f68295f08d24c8acfac1eb18982163c", + "0x9a76cba0046b4e4cc89aa333a0f633147056044624c1b4c1c61a8a62f1f98573", + "0x510d25772f6e14f8e14905b05d8cfd6384561994ec3c92cc76fd37ae6a9812dd", + "0x39ffb78991cfab4236e4bc04325331dd8be2e06c4162f8e51aa592b27e1fa0ed", + "0x45c4d7eed33f4be6f8c0aba3ef42ad6a6e5f654b104895d9d662ea28704d6b43", + "0x21887c54231da9d400b964c63cb2f5378ca3c15f118fa105c0b2545528cf302d", + "0xb71cd47b4a6d40f9b18133c456c2aa7eb2bfe6170ca723a0c584ea28483d13dc", + "0xfb3c2158485851f4f564370c590f4a6b8f6481e6e4736ce9280a89d946b0e016", + "0x5c3ff34c067986eea52db9443be346eb1576498cedefc4a66379b24a3244fcc3", + "0xb27349be4de4d9be3cc9e904f72455c2f131c0beae6757592f49b2f0efd546e1", + "0x15877d8cccfa9f9cfd60408c5a6a173597c2e091a47fcdbaaf0a944a2232edda", + "0xa004d11f0d305763a613cb6d3d44a493ff669c5098b63b3f03e6402adc207bb9", + "0xc033417010a0f390418ff55348055e8b3e676c1d060f0cca2dad80aadc3b253b", + "0x035224e9c4e562b1f0c080ae58a7e9b8d48e565ab0be332fe580303a6b3661ca", + "0x6c7f4fcba6dbe1150b5bc6ebcf176ec3427e555978ca7978bdafa674cd50f4fe", + "0x4f62b3544c9828f9b7aff8fd5991e32b4f18e72c12b1d582c671a71aab4cbacc", + "0x8124e36227c5788e3608c63a93a8a0d966e4d8e12a81e2fa2378a969dabe0ef2", + "0xfffb09addea705c2ae3583b45e80908640b728867cdb8d49b2b1150280795b49", + "0x6ff99c0f0c3b3e208373fa3b7f031b8023b6660801a8ddbde0fe8668310ba332", + "0xfff2fb5a358d3f2bcaf5040e7e0fb9ad96fc918f3be7fc077a8aba08edc64a9d", + "0x3d734511c149e8c1f617fc48fc9edbe27da5e2837dc634ad4a359cb4fc1ce32c", + "0x0c8903bcec94ac45ebe8ee7c5875c23216d9399f7d14fcae79b7af4efcb4e503", + "0xf020a8e3254695e8c5a7b0a952531af6c886719b9354d4142946304220f18b53", + "0x4e1b5e445e923e783b878b943b0d9c9d2427cd8b71c346b79968faaf2a3ad337", + "0xea488d31ea2b67854997c6a88db87a13b4594b429b0dcb531eefede8e22308c0", + "0xc5dd53b4f8c9379c0f86e6b1270b6838d6a6e689865542791917251273c5360e", + "0x1646e9b43ac92494a90ae04647a4047f8de9640aa6f2c2c137962bbcfc80c8bf", + "0xf2f64a9c2e2de9ea1e314d84d44769a114c7035722f5cf7633145c13c5986216", + "0x62f06eb562f3c838c3b24a3da792769e9de8e2bfeefbd70c7aeb9271f19f439a", + "0x1cb36e1cfcfc9e4d8292cccd8d8b8f0bf90db7b63878e147188b16ec19754110", + "0x0316d23ebb039aa69d6b3b9b4603d9092f800b191735abe2a8ea2937b943f725", + "0x4c85e7f1e285380f4d80efd1d572781a93234149b8628350df7d93cb7625155c", + "0xde0010a2a469394e8422f52d128b930aa52a773368c3b8bf38a0dc830f161f8b", + "0x05ef4316554e9e05ca9a16c20f178481b630322c8dd438cbbc18c2bda644fc7e", + "0x64673e06bfcbe84232442ed29179e0b235d29e398e2867a484c2b60727377bc0", + "0x8dfe2659a262b307fb1cfd68fb18925a310691dbadab254c0a81aa9f9ea05c7e", + "0xb0a9fd59b0cbf68d53654d131aff4449c0fdd18217970c3de8d1adfe58d1f4c9", + "0x4ba2ff3b6232b74f661271aa29654638b05ce829083f54be06d8ad4290182910", + "0xe087ab9202b948f3c20cf02cb21f5f1b97d508773d8d7c2e5baf14fd52845076", + "0x702e6b8f663d10c1054de9751594d33f9e0915f1d3c71b36cd9c974574ebc85d", + "0x4dbec1c6654555295d0dada515dc606c18fd72e55b3cdb63ae018b1661c4385a", + "0x151d1c8c60780242caf26e5aeb8860f0ee9bcc4968e96c8c540d3ed0690d9635", + "0xf385a1e79bf23ea44d14b3f8668175342846ebc31e5a6790fca01b52074aa6eb", + "0xf5e94b06a1fbff820e98af762dcbeab9d38a45fe4099728543dc4784ec34b1e4", + "0x2396c1dc9e8b6cb903f97aff55b026511d64b88b08a05c83bc2ffd6becf60ee3", + "0x4b76c7764b5b098c3168779d95ca4aef2b861201c427c416c97b81372436d9ce", + "0x1a5d26c1839a09d7116868abdb2626467ed948bda413adeeb09c1eb146863369", + "0x25d61eb2338186246b78e3a4c4857c89cc6df0c77ec0f19b2d58ee9277c02d38", + "0xb9fab6c7fe62208f5f9c2714747714883987a513f115ba8d4c6904df172e9b0f", + "0x44f2066089cbd0ebae33e1b58179db26332ae99534ae4f864abc94fbcbdf1acd", + "0x97ffcb0dc94906687481210847ad2522c7cbff5758f6242c8a6ead1a010c0423", + "0xd681901709d666912f87058a300cf15b0bad137f4f0993809c37372ae1a73bc8", + "0x1e89c647ffabf82e228aa2ad65a989da66fb9f4a8d0f3efc7d66d72ef75f449d", + "0x40177f69aa2c7f7711c6e2212c2278a861100f1a46a99c340992e776f278bd99", + "0x8c09a1a9e84305a94b702fe21ed39db90d2c0eee6ff21185c555496e99c9807f", + "0xe13af929bc3b6b693936cbe82ffec90cd2d2e177758af41d5c7c7f8b3922582b", + "0x4ed170e95d7b677fc97533837ea0e350a30df6a3862d7b7794e051fbba68adca", + "0x5603af56d81026197df080dc437f9dbedfc82f1acc26f2ade39db63033b7a053", + "0x815bce3f12b5c5b5ef1585ab98a025aaa8117efdb75d2b677abb4522e4b933eb", + "0x603c0103be82ae58d4af604be625016b64c4985e3f2aae31ed620d1635d85a7d", + "0xcec52fdf68b04fccc6c7dc2990412c955d08b6bd4a7a3f0f9fa77d77d1c3481e", + "0x1b3a7d5f6206a15645ed55669485dc4e1cdf7ba41f3488427e8b7202d6175552", + "0x59946fdb729dbda327e825d64932da21571c83e4170c1c7748c527bc81937fee", + "0x676793de267e7078dc3b6927cd472eb1babd4f537bdd8386ee65b88166cd592c", + "0xc5379a7f5ffd7942f9b7590ff83592d91fdbddfa09c040006564434379868110", + "0x44860e8cc625991899e5a5c16d805f6b1aab9e1a79fa462e4ff2f92d6c40e76a", + "0xaa31dc38a358e3a1fc78c4cabbf05517b2ff78e23befdfcf7595074bc0b5498d", + "0x9e7d9885b3a84dc90dc25f1a48987d8d4cc1cd08f51baf1c3f33e86fd17341fd", + "0xd6790620e79c46699c21b0bd61cd09baa1e2b350fddf6d125c4c06fe30a3244c", + "0x4f60d8a3dc5859f69f1b96507527d2afbf899854e72d0d12ef27086dce561a53", + "0x67bd7c84772d709e1276a1863ab4b26ec84eb02767a804ffbda23eba3a8af0d8", + "0x707155713c08309b0ce29e867a9c025e36876935398a87d1153a997801cd7b96", + "0xe94c592379266a58679af9df945fb443b790e1c5a7318a5a1738070ec479d1eb", + "0x00205e95873d1670a9212fdcae09b06fc941128e4db1427fae7b3de33105c292", + "0x0a730cf91406030fea04d74b78d1625a3e5c73a1e7f39fda6db9225e064697ec", + "0xc32b3134dab797f09bc4986a7d9a969b9331a99e431b4026abde8d06a7910f19", + "0x7b9fc8ad3f2bc9e4c680e2400fad27bd0de271f193cb4f7e9727a87e999aecf1", + "0x39d65165378d375fed612489145d41dd4f9d57fcdce1a5fbba35a09210edb201", + "0xebe86247c803ebe65de42feab2d95a8d45775f352a0079e6a868692f444fabfd", + "0x04eb04c01e29b4a0f96cc9072576a8f7f3cda3fbf9b4050f3853ebf763bd85f8", + "0x701a86ebe017b55312ad8277eee062bf0a1b1ea2595631c9c28c740da3d4c117", + "0x57d6119ae2469213c921ac6606f41ed137181c179d6e207d21693399d3b21e67", + "0xdc189a5837c11ead4c6a1b7530b718476a8d0ca879fa4e499d72f7d8fec348de", + "0x96511948f65eed76fd3e17c9f1201e605ad2ee59778df04fcfeab5206dd29192", + "0xdb3930b8a1e2a8eb35940084eb0e98dc53aa1d3aa563e3ca6b260a50cb3a68a9", + "0x806d567cb6fd15fb18007354be55fee1c2b5f0aa1b1e5229ed0e758910af617f", + "0x6e2e8273a970e21164be1e548b92963ab241a68da41e9e231c12ee08bfa4129d", + "0x168e45c60dec2496e48b471b7d2fe1b829e9111191e6bddc936d0787fd42eaf7", + "0xab1ec5c8aeee05f24428c291a4d8afe776a92826f6e58dde95f8bf63e5c8ea29", + "0xa9db30a831b1ff69e38b342ee4fd3c9a8c3f5cf7b647a0c7f5735a4682eb42db", + "0x2b55ec4fe76bde0bc4f8d769d3b182e4ac7fcfd85a28c89fd7fa5d511f41969d", + "0x74ca1b5889e8920833042da86aa8d1259463827bc0cc9e7796144927757e7db9", + "0x5a3d4a8bfd449265b74b85e62305c3184209f12f4f55271cebd7d2df56b9983c", + "0x0b450e356dedc2d73ce19c6028dc5d36818cd3714879e5a402dd8cf7465b2253", + "0x073f03a0316a886c4cd71d4cec42ee7705b6cd897932b6547612f3cbc5c6b8c6", + "0xff2b0244634cc4fc8cae0b44f737dd47db2cb60e05578a34b336c46d0ddc8f09", + "0xedf8c719f93e1bd0cd421bdc14c03f2795dce10bbec5647eb98f5a64da5e7f74", + "0xe9192cd76b33b1d4b09e7d8c3e1ccecdb7a532420dac942934ab76d6491fafb5", + "0x5fa7a590d22896c2ae781644ca8db3ecf779691609dee9d0554ce562624a1e9e", + "0xe6af198683501ff90b9d14a343553e0c5d7c5b9fdf653311e022ad4a33eab72f", + "0x3c28170602ddcb77078d6f715a6629aebe4a35b2498a2cad37057f3357598dd5", + "0x29aa81bc8d9c54f146917aca4b1ea1a0f147f6ced0d23a647a51af942826bbf1", + "0x0056b54bb953c389159a761d3a405b71cb54162481f5cae225b86c8d2f409960", + "0x3c4d9ec5c31c3c087545487bfcda5db713f1ad44450eb310a66d66330b67a0d0", + "0xbbf3002577b95053f241733cdf991602a570a6f33935d50ad64bd31ea9a8714b", + "0xed526f4ea68cbb5a15baaaf6ea698e2d293afe848f3056b803646046aa6d5526", + "0x0162b7210309d0144b8464d21afdbd80b5ac8bc3cf5a93f32fbb87565fd4c30c", + "0xb051288210e1d0ef546dffc2e52026815261e2ea866e1276809000a3e7e3d060", + "0x76397a5e4c77029699eb10959f51ab090437888ea06b1c5d0cdf16b74bc44d3a", + "0xb5cd5928e3dae6cee3ef0fdf7f1deff66628ca8ec29743bf1aedba2e10fcf9b7", + "0x6d1f9aa0f4ab673ca6d0d0f10bdb491882fd1d582c2489eddfe7084eaca0b034", + "0xc636f215b9918856a0ef971a2340dd3d3019ba671c0118c352a1bf64e50f0e61", + "0x8ed6b16c3b89411cdb2a92cd42f2ba4983b26a83eb3112a6a376015458aea55c", + "0xa964e5da59c92492270317ca6a0b86402cb22e0c568dcd20a90b3e826846e634", + "0x2f16af813410d4c3519d9bce71c3c31ff09799d42760f49790f3789a2d365046", + "0xefe1139708e6ec67886b9cc3d6efa9b2b67868c626c3541e7d7a17c75434f2ae", + "0x199135b37713cdf4e1ef3766e331ea84f8e4911552de1e3c133ce9a3a3b52e1f", + "0x400cd17cba12320d710833bc5089c070c482f6fd4188b704628ff47611ca12a3", + "0xaf26e7080fc110911a649b03f8212b43626cf75c867b2af9e70cf35fa99092b3", + "0x346dcdf1ea179f89187527d73747c6bad12ee0efe4c02eda34ad3704d24638a3", + "0xa6df7e1b1a8fc69ad26110c0166383e5d4db331e98fbebb1b56611e0f7539a46", + "0x9d2b7acf84de840c1cc035a355360919ab5599ffce825dcf9598b6c8c23bae24", + "0xf18a92492752d0a118efb2ea386767122acfaa728a39d788cbe1e836ea0f6378", + "0x28f49f4f5a61322658ca40035c0340af61bbde7bd42b74c4a1867b19992dc787", + "0x890dd6356256b7bcdcb27d415c10709c6e898e5f947ca209389b42872102b36b", + "0xa24f6907d5fef0a0c28e79c3be437bab1e4470d29365b9b4779378099a9c3007", + "0x7ce1d586d950a3875954ef50e2b6bacb51e8cb11db45c34b709868aa7eb6bb4f", + "0x5cd316a998fca726c58a56079b8793d208f2324e399413aad3fafae077d834c3", + "0xd367cca6c37fe087c9acf4cedc12ae875eccfa34a4798bf916a2ff1cb245a8b0", + "0x78a1dc488545388a618cfb6eb0047236adaf32c0b5bb3dd35a1843720e41afd3", + "0xb7bdc303b26e920a184e250c398ffae03fe29491523b68e2a9fd658c7f55a734", + "0x72ea58270b1512daa51c08840dfc33e657c4495a5f50bb6ade1fe4e7f2e5d35b", + "0x3af82e240d734c66ecf715b2fa6d42a5976f5451694a9669ed49cb4ce6d495c8", + "0xd875ad205979ae78c6d334e5c2c0c539574172be4f2113e53dce70ac6320e1d3", + "0xa808ff0a2eba422021f62c0de2cb9ea114dcf340fbd97ab0506461578db78104", + "0xc6d5c1cbcb505220f3f47fd100470180620e47cb5b7ea81b4e6b27976ae093e0", + "0x3f788657a09cc34dbf670108046d4debe140154ea8283f5a762b2a6f1927cc89", + "0x0441ef2e2865848be1d3bbf10dd99bd9b307dc6a3833cc8066f71fcc9fd4657c", + "0xa033b833635e284605c2a25cf4b9ed595a8f6b7b6928a60f849019b68e412197", + "0x0c6bf4f59f2369da915c5cda4358a1b84326c94cfde7886fa99c3cd8cce00d3a", + "0x16f8cd5f6cab6aa0968931090a06cd78804da1332ae794954e43e880e6436380", + "0xc44d1853d161e5970cc4b0541aa7498948da07226d9747191d677c1c6c61260a", + "0x63fa006b716a07a172580b605dec90a99f4eb1caa2b6272918e21206b3b08186", + "0xdb0d7fe87810eb83bad89d04f279a06a316e9cc9afd6b249f77ff8da54dcffcf", + "0x04a1e3ca62cc77963b58ef79a9e2ad8ef54216034bd160728a7fa4806c0ccb62", + "0x3db1db1cc10010514a98f45142815604a945544ecb6438c63de232009f525ddf", + "0xfd6619df5d873b255c6e516cf0e12ec1114613a5d803e877fbfff779ec20a1a4", + "0x581ba19ac99cbbcc948c61e0614c64008eda6a58604e9197aa8470266821f331", + "0x316dc2404582e1ab45bef960907fc16e6223639d1875f6cb7dca9c7744cbdd30", + "0xa5c585326fda6f89e33d470abeeeb12a6a65ce9624cf01c5e2ffe5e397ec8efd", + "0xf08862fef3ea5a98b6b4bcbf0b57b482dfa149ba4d5e530829eecef1b5e0ecf9", + "0x7bcb4c42db39ab193745ac34c75e68ab457aa03c83da11f7396df2491a300666", + "0x69424ab7bf19ccf3583048e9c315a5a5d4a7d85359d6b4a3a7ec001be96d7212", + "0xa686da5793c201b92867db72a5f8671ff473cc12617437874644012ff242984c", + "0xf8c11302b69a3b2ad86e7826f8cc85d0ebe21e6420ebab0a7093feb7f15b985c", + "0x6ed81186ae9545a36b50f9a0fa0c82f2f52bbda7e8e936819628629e29402018", + "0xa91851b0638a92d2733b63125085982e5ff2acfffe66f71d3b218ac4ddb4d268", + "0x895ec76e33463b1e9f6a38a15a787ba84812d8d3fa6385824618b7de7f8235d7", + "0xa2c45ceebbb227ca7396e407e26c88bbf0432483608dfd169a085c5372ebbe5d", + "0x8cd8558d7c920df4bff9226cb91dc674d8788376c694ecd27bd84291255b237a", + "0xd9e3c84a853e9b551d296c560048283aff22e0ea3a0853e60505932f06fc0b40", + "0x5148cbb68497c824a428f87cac9512dbe313abf6f4c2b9fe6ee78cf909d4322c", + "0xd5d0a750818cd28793a1238c194503635e07870a65d5a8eae084a6a7d152069e", + "0x83c94a89524371c5bfc66e3ebfdab49eafaeb77fddd5b31bf3eba609cdccb083", + "0x4c3e549fbc7854bb417e50dd281890c745e9748ac7d830acd9c867a2abc64db5", + "0x642f75489711b36d89b0432f92f1ee19d3b8cc1fa99d2c55e8659426068da0c0", + "0xa4a944abbeff1a3d718e28a194bee6bc066f01aafcd0d674919e19c4ce1f473e", + "0xc9336cd020b35855fc7c4c3febdc2a19d4466e22d76ea44977f50fcf00d30772", + "0x0ea3c8090512fea72c5d457e0f3aa72fa0fdda928124177fc07587db60ee4c8a", + "0x4792e45b5fe8a3e2179b0745521a8269d887f8751be3f2c8e1cc8aa9ea0ecb7c", + "0x7ecb169f378c9577eb1af269377c16d25b09dd2aae49705bef1d143b099ffd48", + "0xf9722b9bd88dd8712c2df0826798b903e4e46ebea1caacec33e626b1954bda88", + "0xc7596d6ba28215314a8c0cdda3929284a0f98b14b67eee8bdeeb245a042bf5b9", + "0xb14ae9ee5e30cdf8fcaa8839e06f1dd1c273b9a7f8aa33d6a7f8a2761d6411b6", + "0x160de9ec9947611c32bed5b39fb688fd8521e0be0c992f318e8ea596e932aa85", + "0x166b7e4682502a245b971d27420a50d028febe29a06ada52709290dce6ff65c0", + "0xb3c6774d9d1415508baa55827001283840a3cde35900f0274eb78cc169b20e48", + "0x067c6e4818cb6dc6e098d2f41a0f0d4d555d3079a271e15e24b40c2b33d48c91", + "0x5f43937304d8f1c5915bed915daf11310fe77358a83b3dd2579986e9e63a418d", + "0x361e8400ee55784c4edb63507bc047de04fef5e5c96c4f70c9a61cb5d2fd3f5d", + "0xd6e24a0733e82cdd74b1ac6ec0a54ef3bbad0f68461f804cefe07a4d7e15b359", + "0xa3ef3d7ed01caca083c8cd24b0b2e6b93f307ab3bcd7fb15cc49de6e76a85054", + "0xcb22fe8e323ef120f5ea5f6604ccf7d84adeef7b15f3d81247a676604d127979", + "0x39094ded1f8ba3edb8f9628d72375ba2700942e0870a267ccfa75419231f8ef5", + "0xcdcde1fbcfc0138689d35a44fc9dd9f0b9b43f6bfeb9fe29ca02018ee5e2c358", + "0x75759de7eafcd4114962bde0db7b64e175787ea56aecb37f5b1045900ee8cc0d", + "0x53cc30b0160d81c37e5b9ace29a55bf07beef84f1cd71307b140e2bfb533d0b0", + "0xea48a27931ef94e6babd0342424cafdc835663b381983dd325b00c8b12ef9f28", + "0x3189d2f6c9623c4570d93e4c26ec30cad659f8482fd4bfae5325932fe57afda0", + "0xf0822c0d02fdfa65e48362ca1915c07797df6f87e0a98391ab4046ee4266b758", + "0x4245d5e75abe515824bebc31ec8e2ed9e5ab027fae1c9c74b49e9522c496f00e", + "0xe4c84eb52baf4e9bade65b3711b6bab7e16fd8a21d1162a24911ecdda4ec4c97", + "0xd73d7c7f12b4dc0ef75f4aa6f006a2efd64a6f54f848daa05efaf9fbbc1743c9", + "0x3e73cab1286dcb25621a108bf70103b8a67aad50d03b2a27b15f32e74d2ecf76", + "0xeb61a019301e6d597516b59bc2314f75afd9f8c76ce5ee4b2abcea2d5af65d22", + "0x6e1beb679490b6480aea261f776194bc2943aa7b4aed0edcc06a96472b6a5012", + "0x5c753161fc23f9bb1fc76df8bdfaab24fe0091337a36a56d0f5f959e4a72d903", + "0xc60ec5c3fd1986c8478f5714691a31acf469a5ad47d31e1e6137969328e86707", + "0xae9bfdfb4a7e54835186e3f161249d435420f83a238786f23c261ff0705cefc0", + "0x3a7ca5b57771c703881e05743b76622360ffec308e21b649fa9d2b77eb0485e3", + "0x093ffff3c8d30950e82d67887d44844d21f5d0d333534c65115146d92d667778", + "0x6b460d7cdcced9aa43dd87660e073e6ad6c068c7ff84aeb86decae8a18f0b0ed", + "0x1e23074207fa45234ca76dff1a5b4d78ee87839029b9bf4235cf5484a14a2ef0", + "0x4236c643b14fe734a6fb1e4f9ccd7b6b502ee5d24fee3af1201d6fcd5e2a4991", + "0xebf1cabb6606d20194f82bdfc22fef39e8b73691c802c6421015615cb8651ae4", + "0xa065c07d917d768a40ef583dba8570be0e67c5c067286fa6ea55183ed17aff9e", + "0x2ffce3fba6265f1a82be15315ed6fbaf8710133f2b8242c6a3a3b40da66e98c4", + "0xc814a7b871102332fe8d4daf0c8f7333a9751e3f29b7b02ed4d15a8c217d1490", + "0x29a0b87325be7115d4605df2b65991421f6594b3d3348238a6101deccc88742c", + "0x1fa4239e68c4fb68ec4e8786801d25ccf225336d095888879953c4543a47ca57", + "0x2bc5a052f0832c656a03210175d28fdd0109676e5cd6daf08614b6d9380e0156", + "0xfbea1e39f1d77d1cab4b111962e1b0bc8580fa4d353efe89b95d1ff5f9d249ce", + "0x4e3c512ca97b1d6dc6ad8f0c17e336aadb740ac907924ca6e037c3820d484a90", + "0x26e3d31503539f6fb1220ec960fa450ba15db330a82b97584f48af03e71d3fe7", + "0x37c3e1d006c31c06526b0c02df45329ce4ecbae209231a2222dd835ead7bd813", + "0x9eec04ec09974b5bcea99b021c6fca264f32b3ac895dda421482b865e9d0c340", + "0xa7a9d9fee641431f2b2f217a57a32ec61f03a1c692304768d58d48ee1c7411c6", + "0x87a5638751fc6b716bc25e92292a977f0de1b5f53fabc89b342a3f8c096e509d", + "0x084dfd89d9e63f625d7bc139a9584182937321d0a5e2b61e280cfcce053813db", + "0x30b73b647de41f3e753770f967615d695df8b77fc0e1f770d9cfd8a8b7bfc1cf", + "0x8b05b1906879d64f6534f7231ae5aef00d86c62915537ee54956b2cadccd9c7f", + "0xf1c883baf6073d2244d5b1a40b7e7ab99a22252e6ed34627fdca985ab87d94cd", + "0xb6c207b47b98f0b550eec7dafb5e6bb23a4f1061055542bc1805b6e89dfe763f", + "0xb5a37524abfd343f821cef4a3d6c66d5343bb56dbb4f5217f7c32b888d72c3fe", + "0x326124edc233211d1dec747ca38045125b3891938e8b449b0980be57fab79a26", + "0x4857dc3260a16bba60639a37fe309f46351d2bdc15c1689b6b86c16ee8b4d2b0", + "0x6728195d486a624099eee5c9b07ff62cb8abc7a66aa7f2aa3cbc565ee614a17f", + "0x0a7ac687337258206323a3c49d88ad4ed495e9a18c848a3d8ff51be0eebfaf68", + "0xe0c1882697acb508290737788cfab9880ed09469e4093f6c95e7282f4bdae4cd", + "0x80f763c0e43b60aad58f01aac1275f310b3cde5e3e8f2fb47dc6587b4a5b21c3", + "0xf7b62180b642b826340dd1f69477a136146d341983e4bd875823d804ba205c94", + "0x6e775aba73a0596d398c5ccb9d0adb8d6236cd7dc235e833e69a0a2a369d225f", + "0xa3c65ff864601acedf9ebea681039ae9d19b961b84c39c40561c2ab42b9956dc", + "0x5f5dd206d841a2a0a85186e2dff2f7d862e47e1bc11fdf0c883412643cff72d2", + "0x39df5dbc5ebeb3d6e94e02e90f987070d9f73060e3751cb5fb6c00d737d53a24", + "0xf3a78e92493d0fdf6430671c820949fc39729a366b8b3355634522bccc6fc7fa", + "0xd12bf11fcd9ad2026a340671edf4e0fcdc6d107f338b5e8cd1e110ce8b2b1f97", + "0xf69a0ce968327b516a50bbad9d035e3eaf78e501c11807516ffbe6d847103c12", + "0xbdbe28bf74fbfc5eedbd363cf4cc2792e948ffd66df8006372396ab2b901d9b7", + "0x10fed02d557ad1ba3794d9e97efaa8bf7d6ec86b08c04c1a3c63e099ac3c6fdc", + "0x0735058d7226f806a87f80fe5a55bc6756c96da1fb92b058c08bb2dc6356f4a2", + "0x9c63cf37aa1e235f4fde0cf88c13c3e87396646136de26757dfce9373bbad078", + "0x54105bc5dae2cfc5860308caa5efddaf6d6fa16b6506900851bf8d9311d85945", + "0x26a920994c14e7ca38d62bbecc09c4885acd455160910ad3b778338fe08dba20", + "0xb7d2bd9ec2f0c1824ed85476d70eb3ae85c132545b09b5a3fc89bb647db61c29", + "0x44f8a25051ce2d5c3b6edd22364657af990fd2b327acdd258bd818dcea5d8f10", + "0xc504b1cc18b99305f83cfb82a9452ef1b6650acc7a7513771a9f1b9c4d258894", + "0xae14ce142031a2a1500aefe7d5bb5f2c48fef2d40bcb3cebc4465b5f588dec0d", + "0x26a6a67c3642f7b63505fba8bc8f4f2db35875820853a7e93b7015cb59ad5288", + "0xef2cebfed16dc3ec1ed7adf4d31a907b1c48986f3bc678f0e0d42260f3e5366d", + "0xcde8bd5a558e4b4aca53d0d164ab030a9206e0a3dc3d52750454cd5dbde67a87", + "0x80213b7c8404bcb599c6acd3d0c7b97c821f9eb36d1a261c7d59c66aaf783557", + "0x84974ac71d8599e8b481f4ef1ddd2365540c97d8a14bc55b036477b8b930a1b0", + "0x22d700ec6faf569754dace5bd720287156b44a7c647338568df8316e4a3a1590", + "0x0f174bb7672e56e087429fc4ada9fdfd4dea9f94abd976a2d9c1b3b2103883d8", + "0xa31dce1e1597f2603aba9b84f545c56947496b648b770ee9bee76b26b076ed0a", + "0x3bcdc44880d4cf88f9d3a107bb254607cd799cab841346ea3dd2e10dc461947e", + "0x54d99c8c46ffb8d4a6c2453cb2ef92abbea6cf8d666f627afcb1f7383e248c9b", + "0x4bad0aac0f99bbe49041b0d7b7da2d0b379cd9a1a4e845f04ee6c399020062f4", + "0xadb0bb1b69fb15d78f835ef5d2de20273faef16889952c0578d3be609573ad68", + "0x04adca8a68fa1db84b226f2e46d3328be74cb9596e037d9e2bc612e5bfa0910a", + "0x9630ded627a97f9641e9606a5ea8eb5e04e0a7e52dea484b2498049e26a596c9", + "0x146544215d83359045dd52eb7821325178f25f950585174c97ce6431e84c432b", + "0x743b7f7b7e0e4bd94ce1cf7fd26a42fe3e60946b46f12094336624b22799abfa", + "0xbe5842fcf8d27fd42b2dd344f4fa80a5ec75576ab0d3bf7df076d2c62c87ee63", + "0x0913189b9ea0086155d331989622986163ceb9d8df937b013466350d5b6cb9f0", + "0x179863d8ee06cc5012fde594f93fabd674cd718e529214acf0855ba7920e0e5d", + "0xaba24275c0e3fc84593d75c0ed0ef64562b2ca29a63c9651ce2972f466f588c6", + "0x59953a3a63270197712f70bc8c1f3fbb974f3373967c76c6ecb05b36624c4e9e", + "0xb03b28ea463ee70e33fe1431556e4186a773476100a61200a72dae7b5a917400", + "0xbe29685ad0f0e4f4e06f7aef6ac68716345a503096669c02f9a4cf2645b20fb4", + "0x77349196f4b655fdf90ac091bf217cb4afc46efd8b3738438fafea3ec8bf6afa", + "0xe0f3c1b6b1f9a822f765a8657ba73b6dac6856b86d73aee29ac8c67870cb5d49", + "0x1e9967c1aa6cae9e6d6ce588cc7be4458e30f004f07f0886bcea9f1894409541", + "0x48e90887d177e254c035c4e6341e43e2fd4b69934f357ef0633a4876af886ba2", + "0x18b962c2749d6fcc59615c7fe48242c4593b18d9c777bc8929f663bb2e500a62", + "0x437628a508e2589d87073ae2cce6867feceab7ed948913a5f367aba70ac752d8", + "0xc76db66e84e99a50d9d43255bd34937dd98666850f01c7abb61a59c61c48a51e", + "0x9c97b43a11c959976be2dfa43d3e427c81c9f65f0cec38a30174753911a0aa62", + "0xe7a8db25a547dc1ec612c3270d98ecc5d358edf0093e9a47f97bc162e2563c56", + "0xf8e9f99e26b147e30bd22a71a02c2e87a4f816947b23be23d7a998bab0bf42e3", + "0x9cc232de33ef4d9c2701403ee75627560a4a290ec00e9fe2bc3e48d22729e4f6", + "0x83b328313a8256d67a76a199607871c9d7d91ac4bba03e4c7eed2850b863f014", + "0xf785f6f42d160759627f956c381c0b2afe133191361126e1dfdcef8ef96a19b6", + "0xba15c02abdb13795b7ffbd05ae808df3ba6dd49d3f6bdbb7594301fdcdafefce", + "0x7cf481f442daaf09047af092254ea96b3e6fceff47e7354dca59ce92a7f0192f", + "0xaa09cc16199e82bf7ca1c664df1683dd68f60392383aa86f7a4d70db4fcc9ae5", + "0xf48584eb70849f03500c2992ca9d8a25c6b4af1dc6b267bf96b8daa844653671", + "0xaaea21fc3ffc8af49d5b6c8ff06d212fd5cecdcf1ee8bbbf11081b610e5efd56", + "0xc3b611e4a4769a4cd86fb97594b05eeaa1438a531661d0c46498ad1d5ff0533b", + "0x86ed5238ab44b097b901bbf7a2f76c4e554227377fd05cbde62ad678b9b40521", + "0xcecc96f7e1da3a85044251681174c283cdf818549338d0ac8c55ac32941c4c52", + "0x8e476a409ebebb50a6b4ea63a57fb97a94325048b98e9038155f44cad234c5b3", + "0xb0cdd01f69adb4482f4a95869c27e7f488b8d645e9da2a1eed7c50db91bddef4", + "0xd59c2ef502068560309402eeafba81b6104e4481d08d294fec8f4a14ebe421f8", + "0x117637dcdf602facd5ba4ebfb788cc6e894172a41ccd461371fb6dc3b29ed1e3", + "0x2088af0cc8a2d219cea2658be188bce545e095d526152531ae04a0c1026e15f7", + "0xbe9d09c236fc2464f9e96468304cb107c1015618c0777414048260ecf3073a80", + "0x0e21d49b429787ecc39920a7b1e57bbef8b54f6dff555afa96c6d664eba18002", + "0xe93a96a4f59c51c991822d4ee0ce843f194df8ef1df1aaa3b15ffd4a3deb2902", + "0xfe432910348f2ae69089ab89571297f755f70d9e52918b761b7cf059bc8fbfcd", + "0x7589450a89b93dd85dda9fad10249d5662653eeb34135a8c5dcfeee2723e714e", + "0xdbcbd8f60a88aec563061478b12c5ab4ceabe1deb3f2df53fd862fd6742c69ac", + "0x4c77ea8aa197a0446c2c85bac546f8eeb7bbce07abb092450f11bb3fb6410575", + "0x465ecdf71fee3c0e42f2dbddafa3cbfb53f4c9a886777cf13dc55064dd29b07b", + "0xd220779d8eba431d33a23b369241b357b399875205cc432069b445426d7a3099", + "0xc6b84a981cf211d41e16ad44b1e90fdcb71a92230c9ac679874f0cecaeb25254", + "0x31841dbf1cfba19ac78cd40136a8ed9f35a77d48fc927e049d58d62b3ff7592e", + "0x0dc4eef3839d33dd5a516a3d4227a3ce23a9a5532d146f37dee88898ee75e7f9", + "0x6859f52e3c1ceab008c2fd85fba4901d7543c99ce5aaf36ccd6534f4898894e6", + "0xd16539f006277c2a3dcd96d3f7e9fac7d5bb0d63afb1d208960e597ce29ac429", + "0xe0ef33cdd414f24ac2e67ab2329d3d728f3924c50032857e57dcc9999e37a182", + "0xf50b311a95bb72916c6d4dad0db9779144a85915a12f895829ca5de9d697fa52", + "0x3f773b516c1779a4c5cec58f6e3a0c1bbbefb939db70bd84ea4370548df29c06", + "0xa3348605b4ee98db29b6bf80eb8181f4200d691e27345732cdcdd09c395f0256", + "0x4f2970a9d6dac5eaabfbb17a916bef4571e1147ffb5e241360c6941ec5628815", + "0x1b7f448f52b39279d47b572e55bfbcf893e7d106436a46db2a0ecd888d7806c2", + "0x89196aa22dbf319c88fadac3c051c445c6d59eb60a62ee71158e4893bdff8706", + "0x1ecc71d15d386ece87caaab915d0a10ad763cdd38bdac269108e17da3183d0ec", + "0x99b80ec779eb253869cc4351371ac8f1896016de0c501271a0a15bc4130f40e0", + "0x09d601e2ef7b509d5343a8b6de63d70f99a453540138d0596b80e2f39212fe4d", + "0x2e0412de93ff148a1365719ef562f54addcfaffa64a630d2bebbe48275377d14", + "0xfcde011b08437d8db6c4e8661b98ec829e204729badd675782a7792648a32c40", + "0x075e3439595f2e5953419638c391ad68d634da037df91cd75ab062f5635232a0", + "0xe1c770b2b8f17b25736880a7d28e09845626879f61fa7bae77202178d26e2db4", + "0x10eb4649692dc022f7542da5156c8005f2a9d6c4b0ac81b5ef087bd989a83534", + "0x612745b3b43e42cb44d302016ab33eeb1924b8ed1a1bc4ea86985d88184a1704", + "0xbc23be71d7cc509307ebd055d02e396e867e74ff401583b5801546b12cfbce30", + "0x55be4d6f914bf5041a111b949ba61807b53707ad6aaaecb347e9bd53251cfc98", + "0x6207ddddfa073ecbeac270e0490bd4f1b126e25523350e45713f8f76074035ea", + "0x7c2806322ae925abe44606def3a91a9546ce5557e61517d22c94a6eb358aeea8", + "0x2660eb9fe82891120afbbf8d17c3a0c6e71a7373bdc961b398fb3924d769534c", + "0x8e94fda030985bee8161a349c74a10715ab29b4c404c4200332a124f8e6c8eb6", + "0x77c264bbd45e806388df32d1d30b7567af8463c310b2e00ac075f202594df289", + "0x11307dbd441966c22a78712e8026ef0be95c009a075b1d75881f45a9547c9469", + "0xd487cb10ea6f81c67836eefb9cf326cb01464b8a4c640359da0ffd0dfb5ddfab", + "0x32e5ceb716c69e4aa115de40404f0406841a43b9d1a7503629edfd830b8070c4", + "0xfc69abcbaaac051ad68462b2b6fb289f093cc8f965a31e4914fc1c3ecca0a62a", + "0xdb2c7749b7f5e8e60861730a3f39531c9fae68bc6d5cfd884b054b14ddfea8a3", + "0xa641637ef64ad91eaa7cb24851870874c04c74e3c57825cc00bcfca56cd671d5", + "0x6e55a7184ed79701a582293b72c847df32be67859a34cb73d1efddcf92c7f1ae", + "0x0d30de75a74723ccaf326a096048686a6236a5d9728e398b531ea34a20273e35", + "0xaf9e881b47ab25b2b667a9232cf562ddb07d30194f6a3358cd6aacc414e98f3a", + "0xbda5685b8567d1c43530fca95e4c671d4f090e50d0ae631eb7082f260036d5d9", + "0x21fe5a7e105e0e2f68698e20f88a9662bceb20bfcc884d493cfbed72fc78ae8a", + "0x1b45a77d8ce14a769f196ac4a195207a63241ac8c8ee42e17c3e0b340b1c1492", + "0xf8fc91a39b6e08d874500e240a6abea002794f104592d18ea01449589b06dc22", + "0x45d9330d6eecf2eaca322e83df77277c76f9a95b99c5fddd55bd77697cd1159d", + "0xbbd726c3aedf917c4741952ee3a21d3c1655aa27cdd98f44d15dddeda185c520", + "0xbce90dfdf79213f5e6c69adfd7efeabe74edd6e2f07a1af7324cea9d70615386", + "0xe50e0dbb2106fa5433506694325d6ebdfdf5270c17bfd255bf3e5e7c0177015f", + "0x22ff7ec5117adf2f0aae8532dd371ad9d902a69babe3b26cfce4a324854092a9", + "0x638b7c73e85e4aa5074daf2daf66c002c00de5e8196c2c01e6945d7aef6661a2", + "0x322d5f71ce4a0e743ce017f6ce76944023e858109a0c36fbab41c796b6f9c8ba", + "0xb55e7340a551bc76ec89ff3ce1c7bda807c75b6a89f0e97ff4a4ea59d232e91b", + "0xe7084bc046f4975a04be1ae50f4e46e69b7d0a3a3886e60f989ebac4a7d3ccd8", + "0xc540174cecd421363ed6b4a95fc554f0ed0a7e4622cb2c0643555eaddebf287a", + "0x03e84b7ce420aa276d9adce1da92c950688839cdd3dd621f797eba5b6986b3f8", + "0xb75090e43bc5790bdfc86a5c9eac4384d099daef3d40e9fde443409f4efe91ca", + "0x7e72b4a7ece28fef076331bac790e49ebf926423f2414c9cd78788c8cca28761", + "0x9b9b702deaa509fface95de5048fb864b80d7891fabdc7a9770af1b24777f5dc", + "0x1b056b381e1c60a3c126f2e5c4ed98252755a5d3b7f3528584dfd128eedfc89f", + "0x8711c4892f49f2c1fb0739de8eaf08ae8c8c279ec73e42fb4da5b25f22d73fc6", + "0x70ae1c1cd310003a4b4e36dfb0dc7351b6f49aafa2cfb5d691fb59a724fbd387", + "0x796d8689110932ef6582af180818bd11bb9df2b06ab1107879612dccc190c334", + "0x58ba45b6ff40da3f3b76da55a0ea9ca63aa135ce308c5d262886e6b1e8869621", + "0x8c2a83da544b209010c6b52618c3137c1066e4fec0e1731d6be408ee881d5844", + "0x1591af374ba5612ece1d642e9bd280a9318c5bda73ed67bc1e7d65ef5616cf6e", + "0x666b3f1a401843af2bfde18ce659965b5c200972da47752fc2b4bfce55625af8", + "0x23331ecfd36f8dc53c388ec3d97a1f364a55b8d701389fd56b867a828eedd187", + "0xd995e1e27b596233e33acabfe03adfeed6d34b421b16d5700fa4b3c234b91ef9", + "0x819d8d3a0471a292c89d0093c3458d12bf83b60394f8c03053bdde51cc08b217", + "0xca27ca64436b71096f37d6e0442a9a93097029d5f4b6d042e2a6bdb3edf94103", + "0x65d5ed59626e98c2bbd6350e4a10e3e45d7479eb2cb6538c5e020fbcb900fef9", + "0x812b423882226db0bfcf2bcb9f1bac80eec3388755dbcf6b613c9f6faf1517ff", + "0x000182526c28c1fb862e74300fa89181cb137a39dcc02fea67bd3a03900fdf4a", + "0x1cfc60d220c883c273603f885193858926f072d3e0094a337d12268007644d5d", + "0x453d43c8dfc8df47751045173fe9e5d200fff28042282d67c0ca9230728329be", + "0x591091aad61a44c41c91b9ddbd1b6159fc9eec6e9c5839c6a0252dfa11436e08", + "0x464a2b049caf36d77652c2d721850d1f8db2fa70ca0964c7f020152612bea380", + "0x8ba06aad1a433193692ffeba643a481e2932f8a2ac35ebeb04c9ff81866d294f", + "0x4d915cf4c14d518463805c6bc92edbec4e8e8689e141c542d743c40640559bab", + "0x0e72daf5ad4857e563921671bcc26eb186f37b9f9d44655341311a6b529200ed", + "0x07ee736bd010d419da4e5c37ed17cf5739a56a78947b4257264acea62b5d6334", + "0xfa03e571d821780ce501373b529ca3de36a53f58e197d64b57f0941288487734", + "0x3293babf49116d584283c915c0c8a26f9c696eff261e74d107dff21e8b2d08ac", + "0xb2f169d84ee6d4ba54b1d526282ab04b7539f216e62362fb20a569f129d2f89f", + "0xdfbdf7d63f67a6986a6c5f9ea2b49d375147b224dc1e489a45fd7731230038cb", + "0x827d46b120f6b1501977fd5ad96fd96090fda549d59922d8607c90ada10ec8c0", + "0x8e15c4714f44297868b988e72b2326eec72e148a849cd39c0447862a9f34fc9a", + "0x311aaecf73140d37dc35479828c96923690265c3591fca9b01b8cad0aa411f23", + "0x47de3df6c970ea79568dc7b2c65cdf8bb630d427d67ec9552cdc928b749a113e", + "0x72e70bd58cd9fe64b27016ea29637bc6d5f13507a803431a14465cdf371dd9f4", + "0x46367ab41a9815df566c20f6f0223ddfec61f02e087e5498402688770739f94e", + "0xf3238ded6fc3b6f389a84766a8ee6663f26a54c077b49ceab1661bd08b4a37af", + "0x0770db8ba56663e8590afb552b42e4db3748e44ff33cf19c483c2bd5cab61742", + "0xe88be10da232234c33a267b9b534417385bd21f9d670fdc0b3e73ad155871ec4", + "0x725cd4f97cd34753d7c39a5a072d2de361f41a4489720a99974df1b821e576ac", + "0x10778832a2f1bfb25ce014c78a2aadddfa245e5e539719fad270cbbed5958e55", + "0x4467fe919d607e0dfd78119a648aae22fc56e508eb68052ad1c9bae0d7c5a2e0", + "0xedcb7a0986dd70d8fed81c4a50c864bc3086a3b34ddaf437be7f445f454d7c88", + "0xb278f42c1518ba3b6d6aec679936d2130915f02f8002f8563b855d7cb3b79ff6", + "0xaa4b012061ef7e6073c95147a118266c6d3417ccd0133eb57d7fdb7b1ba3a5c4", + "0xaa263496af99304030953d11d771ae5e337bcc24050e9c944c9e89650f6ae256", + "0x3f99f4bb34f441ea82655d5ac40ae0a307c6ea000ae1a2eeb2709ffc0c624362", + "0xeedc934109c18a446c42cf36bb5d0b9755b439eb62c18c7d334c13c38d379707", + "0x663b6e16d36687a79cb14792580fd32a7c35bd15df6e6fd36bb498ca12b98b93", + "0xa4c2d103e57cc53f50c63066fb7802b6d77c474ef603e6e6b8ecd759c8161737", + "0x3d381365e830f7fcefa8a466f348f5be81a3dd5c72d918501881244417c67594", + "0xe3ee1ee45c13527eada86f390f69d112b97bb0bd20b40e609ac6be9dee116de4", + "0x9479836ff23cafc57b92900c5349180e90afc75571ec738021dff05f9e2681e3", + "0x7ec50e215e840a94aea029294fd6a48d27905886e53a1239699558631661dfd2", + "0x8c58dc1ee71c06c9a94d5fd15aa8fa4f7d90c8bc8d0eaab7d8d0bb2baa9ae4e5", + "0x017a3cf0c797ceedf53dec3cd522ffd6ad4f10ebf709b20dfe669ba1fe0a3287", + "0xe733cb71d8dca2d6e3c71d9cfdf792c8a6fd242af91fb17bf599eea1b6b75cfd", + "0x6e820e5269a6b6fb9e9c2f477cf796078a3c2edfb7ac8b99150d6660ffd22f0f", + "0xbf1d3b3dae3b10422e2c354533f8bd504b1f5852c942618a0f6b415c3b162175", + "0x12fc9bd3a29e5ceae601c6a4e7c92bfe6b22684cc1f828dbd2127ea56e1aa300", + "0xc19e28d20c121a0345d89b097acbb6c15d97253895d06b8dec219f11f84159a5", + "0x73f03ac8b09a769db2a30f44b77d4d5dd6d61fa97afebe6795acc9c5c10cd7a5", + "0xa92cac86cf8bfc4fda37a94e5f55763840084dabe413f2432c4b50ddcd3523b7", + "0xf7eb6d87cdbb534507ca9ce4cc42a03eb843d608d6e2bb42c79bde78d0dd05b9", + "0x3bffe93f111e1883a29b897fc4b33c50f32db2470d819316577bc38e1a5ed179", + "0xe44f4633ce949bd8312b23178b6ff0e3d72b30dabc546ed5b5f2dd97b490be38", + "0x5e6dd4f45c09aebeff5c87b5e8c3b7b77ce4991f230dc7f0dc62f460e684f371", + "0xf1e0440dc83803d73ae9a3bb6454a824194bf9ce4670adc598ee97d1e150dbe8", + "0xef43649067273a56948dcae0271f9a336067610769b2ca4553eb7bd891ff6731", + "0xd9bf0ef6e8c057bea8046402e5bdf0977eb34bfbfc9f14a37576a8ba09232dae", + "0x38196dcd11fd1c406b5201be0286eed3a94a263a1990b66272c9b8764a5f2f43", + "0xa7e55d25456b2cb9541239879d751e489adfc4c0e232445a9232f4462aa17294", + "0x379ea9b1194e78792ddb00740e0c87585ab9a1060e6a2239fa6c5a9130a2a490", + "0xea0d1449333060d955f4dbbd0c0e70593489585465b37202cb4936164296addd", + "0xac2adc5be7eae9e0a63b5758c964ed5b203f37309d92ffb1d22aaae041fd9f0b", + "0x37be79f2f0ebbd92f82502d12dfe42f98f2e75517f444a295426b6829a6b0739", + "0x51a86fba7b41a59b84cc56d264f5d6d83a40f233f7d316c143c29bf424fbf6d3", + "0x248215d9d66288e4c2ee0c182abece81ffdeabea2b16eb986c14d10d69ae4efe", + "0xd8affec4833fd827b2ec6d4c1905e8cc3266e8a825a7502b59585fe7d5ee653c", + "0x00cdc19f0fa69766a3aeae1dd17cd9f61afe15ef6128a957f72fb3cec4cdc02d", + "0x8c8b43d75591539f17fd442b604a58763abbf17a408958ca181c3593dcf0afe6", + "0x055f6cd0e80234feb885657182cd2fc44fea12c6041d20cad8d257f2b4f16319", + "0x487a48fce29c08736e352e4217ba5714f361f02fb8500ce6a2e9ece79b1ac783", + "0x807974b9c1da9879f8967e7dfe02e8e3fa23d08526963c8b22688ef10de36374", + "0x080fcd507842ea3176b14ca52d844df2f9796b4f1281f7d03180c0461d28b801", + "0x95e5e32410daebb3a023678845b2e1fb9097161681c150450c7b76fb8c5c1311", + "0x420611006cd3a391071c6fa22e82d836b5db225fc266b99e126934d5fdf4ac84", + "0x4bcb71688e5ac4271354485f2a28a3c63d67e028183db2a16b15dd718bb89762", + "0xd428d57137d70ec31c6299ed366f69057d9bf93ad06e62332db04db5c3cb425a", + "0xa384c72f5ac5864dca1aef91fb19d3233e4ae3333d68bb320a583f532cd7766e", + "0x9df6e7ec6a1f455ade59bf1cebf6ae1ffb94d1b05249c35964aece90f2ec9f80", + "0xe53ec67b4d7c04b2c7cc7abd797d6a7c8b759b24c8389d103222f3fc83ade921", + "0xf6031d3cc54fe050642891cb57466ad51e0e8f24a8ac52d592e531b0fb87c971", + "0x842c6b20d8a93c3b23e679094fa903feee295c933c9092ca444bb12f966dd650", + "0x38b30dce467bc4347f65f7c7cc4c8a17f58d58b1779e302954fea9e061bb16d4", + "0xe23303957fac2f117e5749e48e11842d6f5a03e97acefb4667630d281351da5a", + "0x47a9c1072f12cf0f42d209cdd33ec885e6e21d13587cd8f75eeb1f879ab55f05", + "0xaed88507af6c779fdce8b5afe124a51528828e734744d8c12cc289c21d7eaa47", + "0x1b6146a607cdc5dac7cc1a88f260ccc2f5883c8a95665b2c9761d9a119b09009", + "0x6ed83952b5bc9b18ea070cddbcca46e42d1f4419c17a51a2dbbe47a0fe7df731", + "0x7544a913c1e6bebe68a6c04dc903860e5e6fb66d4845dd0d98b3ff4e5c33e3ea", + "0xfb6eea6e7399be0c2673dda0ab64a6802d6b2eef1550341d894f1f4146ce9270", + "0xddb39bcae9dc685e7bee0757743f2dd67b56d26247931f4f184f9e8f0f603f28", + "0x363c1bca93f3ff58a48f7d233bf4617769da4774faef4154abc84a99f39c05d0", + "0xd3a56170fd60ab7c75b33943a5f56ec935471b45e9b31825da160498eeee4c69", + "0x347834e86b3ae5fba9a7873e36bd780317cbf7649c8c01875dce04db8e97f450", + "0xe2295dc1850609e564a072b4592b62ec6a0781477d929c9293e65ec494ff027c", + "0x4ecca654f4c6f701c975301f051efc05e9adf0e908fba6dbeec42dc99ea0c2f1", + "0x6b2662a790d931d1f22062a5dd2518b30d980c84d3bf5b69ff0182aaabb124cf", + "0xcf838b75182078fdd33855bec6cd789f0d4975edc3387aa04246cc73f423da0d", + "0x791a7252fa21bf3183b621d65d6a7b584ce1de93a05da8e44a52572fb72a4b4f", + "0x82cbedcb5b48e87ed79935f8a2f93fa5367c32fb14de3ffd2e8947fc8a61fd76", + "0x84a1a28a08d0b6cf71128636355ea3a045d564cacdf67fc29868deb32ba0df3b", + "0xf97f154b9d62135748f94d714ed0b87012584d784df893ad581c36b5b04ef2f5", + "0xc03cec8562c67e6bbf59f97590560141778f5a265b314a6213c440f5703db388", + "0x50735a0e040ca5596ded470e5068b9b7d93676d8f3d181edfc26b5a52981cf0f", + "0xbbb1b6b1366bef92cc978ce644cfcb951ed00d7969e7d89b77bf2de745210797", + "0x116d30e2696ee09337bf9e1a78ab4d8e2cee7c53505afe2b8f027cdeb34640cd", + "0x294855f629441397876e7a0649a1e5ec1cc3ae8f425c24be34ca45e005bcc3ae", + "0xe54763eb5c965fc0c6ff3e975802f9a5049fc3404fe790c31df8a3dd5b366a24", + "0x0413d30f913089871fa15d643ec78f2503d13d025d2b73a6b1a879a910da2ec1", + "0xa430e9153e8e0d9fbb50f0764a0b70e5add25fbd6d5d29bafa77ee93c483e4fd", + "0xa4c61e3928031abbc4b02a892f19706a313bbdfd5bb413eaf852ce84005f186c", + "0x4e242ca7106cf988584cc61bba6e2185f6b6c377a2c4db291e6a801a5b66e1d3", + "0x23b37072cd63dfe8ae9b84b9bb74130a90bd603a5b992ce78b6e2c068ef35b43", + "0xef7e50ea520ca6a595541606ad895beb80bd06cb68984572396e1ea0ceb2428e", + "0x29e48294e6dfc86bea2bf76ee9daa816a59840ad1c0878d77cca43a67a217891", + "0x419809205842b124efb16d84b5defd6985b3c774fb6bd443f1ef72a2801fffa9", + "0xb25461244545d4b6f9c1c846dd09dbf25459d6492b1209814f22a3323ba74832", + "0xee26501773c29c20a4e860047ce14e4af4f6234736e1156002ddfceadde41c79", + "0x3705a24875542eb6b893c0b48266b141af1b5eac2826e88ca4645cff47a75400", + "0xeeb8999c6eff73cee7ae33ddc065e3b3857f213e80a94d832693356f0bddf504", + "0xcddca06cab74354633b9fa50fcd22c838a9d69a129fbb3c17dd0008df9b23524", + "0xfdb23fdaaf80f26f0e95aee174446f7e7182283d652a332ec075d86f6f3f8730", + "0xb3cf34c2dde0952c0d46633ec3e4fca78a3ee8242f2b46b4fc4444afac7d611e", + "0x2d9485c467207fc78b2de05decaffb155b01446050b7a6679923181dc05a646f", + "0x508eb22f40e6e60ecefdde86c50c81f9e73253ca0658a63ffee65f6fa39d898f", + "0x4af2ccccefa90b3028576e09f522bb667a428560f8c94cb159477313b551e384", + "0xd85f3073e44e61bc52696ff86194679a1ebc3e7dce62805c8b03372e03dbafa8", + "0x86ed94dbc666a541088ec977764bc543ae892d73feb39b0b09dd47664768a53d", + "0x36934b9186a56e71ed0c0f51331a99b3f16f07d5b50a1b770fd0ad91562bd0c1", + "0xebe80ebae860665714be6e5b9e8e7cd5608c8b4466453a15ca2fdf5d21b193eb", + "0xcbdccd3919f1b52732a89ff592a6f1dc34da53f5dc409cfec7fc01ef1f61cfd8", + "0xac2a1fc08c3d4b20db35ed4f879921df5e61a6596e655dfb2c6d5ec916ad6bde", + "0x4e072a9ed4432b5f205808f79515fa1d023cf0e6c2137a2a8876b5aaebee28b5", + "0x096aaf5a90b5e6ed00510bb1af1c475c32fd3de2c800b281d2558c20c0288e08", + "0x0f60a5da86c795d88bc025909c8f4c9484ea6489e900c4070d851e91f27afbe5", + "0xc6532945ae1b3fe27002f5d035eff11c62f6b1e9866f23c52af52cdfc8c19c2e", + "0x7404e33f0864651428778f7a39fdaf11a236c8a68a30674d619546518c380928", + "0xc44a5ea175a1d64b02326e7e49782968d9f808a7854c1e338314766bd96b95d0", + "0x435c5d7e5f41b84a55a11606992a4d8ec543cb5062b46f897954035755f9c9ba", + "0x854e9945fb93e485d4da05069f354093750e8eae98a4ef140a4018f3baacd246", + "0xd9e43cac6d5ec4f110baa3c940c5cf35125ab44a102a621e78d824f1abfacde3", + "0x99e780c379a3a94186a99ce600fe88963e58d61d851c6437f7a5ec3c2a4cf6fa", + "0x32786b25ba696ca57c302d136bb7453a5dfc065fcd17e8c38a47a3806e8d4990", + "0xe4d0e757ec64fc8f485839bd34c5caa72ad0f33405fdea6fe437280b427ee6b1", + "0x65e36e757ffb5386f130d783da437e3c7eba37e80ecb8e3017fa6753cef1753b", + "0x3b5be8acf141f95673e207a7c4df7c81fe88767ade0f785f5165c7f03e284008", + "0x988ccc28f0fe0f667ccab304d7cd0d56354419ef8b7149099d1515aa384a0ab2", + "0xeb198fa97ce093081073e2f6d531df421a9692d086387221f215677e9626c6f0", + "0xd6b98b2c6ffbf467dfd0a6365ccc478aefdd8872cfff6f8176023be4ac1d00bc", + "0xa14078605f856243d5195898a5f1e91878bd5ce04458d2a64b0a6ea7f608a18f", + "0x9ccc3a6730ffaa7488c75a31772c956f911eb7002fc245d803e2ca8d45810a13", + "0xc220e09c2646b44edbc09b43748855653861e2211eadc84e7a9cf9b233c85483", + "0x64ffdfad8e5becd8a87aa11a5feec484fa9bf13ea9e73beb7d92e396589a4f93", + "0x791b62a9fec44f975f11e5e5ee56ca70202d0de728eb6a7acd7c40cdeabf0088", + "0xe0359eae31308bf682f09147977b20f40425ae465fddea0515c89a75cd569305", + "0x16199dc2b3e744a4928b767467d02053576f98a5963a9c123dc760c0e4944987", + "0xbb0a149368f44e9b033245d758f1d906d061bd7b226adc1ddffb9f6118e85339", + "0xc600086572f4ecef9f3388a1fc242b099807e61463a934a42dd6bb683193b8d5", + "0x5adca1ea45a4113c166eb85779efc6836c072293e47d2c746b73b2956237b05f", + "0x0bc8f8aa233108727de0e439264f9220a6d5f085112521868c10934dd4100f7c", + "0xeb9edb2bfc6d973c762e283348290da7f3ea45ac449a521e0b3bc41e818aa702", + "0x4684a177b066091979171a6d0ad0a3eab2e7fb19c1bd173786d2db8b1795e49b", + "0xc877565ea3cee7cc08aef811c986cb58d31358c5bc1595bf8f41da9d2b6833df", + "0xe56178ad5772adca0b3f5eebdaa03af33df4451e2797892d12ccc98caee4f401", + "0x3f02d423f1769519c746b6ff632ecc88f6c6161300579c05b704353a13225814", + "0xc2cb58c5152a16f20d9489af5c259483e46ea76f76778947dadf653fb17703cd", + "0x9598d6265c31d75e15323b7125eedb124361b08f1e695e573b4743a3b54bf137", + "0xd401867574d59042a58837137cae986c1ccf45051165816a28f77274d78e6e1c", + "0xd5bd34c381e34dc538839a4a20a5de9e5c87d944f05f889a6c0bf0abf39b583d", + "0x5802fa750e2a8b4ace972d927c4fee3db2c1c3e12f52a20bcc17a04d1f15b01b", + "0x9b13a2989252a0e796ee57e61889ca55bfcfa49d9285ec859e20ddbc8ae59f57", + "0x134da834b7cc2f81f2582366d270d566dd95fa1ad867c795fedc1d74f1dd78c0", + "0x3cc3ddced0ac6b0833ff5bc9763e5323c375a6c32d588b53b98c7e30b2660c05", + "0x8f79d40ed56567479f2510c4e1e81edaee9eb15c3fab934bbfb6c19583032f45", + "0x423e59300c8a57adb8a20cf17b6686238d3d24ed6b0887573539772d209c03f8", + "0xfb633f9f16d9d9f3889495193d5d5250c533b2d2feacfa8b77a598bd1573e59c", + "0x1a2c990aac12d4e85479edd998b7cd84419e64e1d64b71b6e5de354ac220aa5e", + "0x7d37124ba47aed15bd9f12aba6eb1e4ba760965cdb0b0b8049a2dd1a4590bd0d", + "0x5305defebd2a61fee315fbd92c33a971f901310a837b78a89132a3e6757d9ad5", + "0x90c9890e4e15d5285b15596d61471c5e699222bcab2c40d613a20b1e4d0a448a", + "0xa9f12a1a2b93a8f62ce5c31eafeaabd0f3053ca10382048f5493e2f4a3def390", + "0xd32998eb20d224ebfa0f2c87c23cf9cf707dea92f12dbb5c1cce2ee1ef010573", + "0x58259819f210344220294ce925298a770d4b945f69bf6e310d74428943d513a7", + "0x7a7e8cf127d88a93a5ab3e59f9fec2e0a35d9ddbf90396830c37c0a29352099a", + "0x27a39ae87afd6ea727aa75062cf53d35aa420787f0679a9817b15b77c2fd4243", + "0xea8404bc312c47dc19ecfd815780e1fddabed7e1c2268194de2755767f8c6c2b", + "0x2726489dedccf450d85a877501537c51b0037fd7fa3f46150bd5004160c56d24", + "0x1a6752f65e4199b1d7bfa5385a4de60ac0786a0c2276fcd98ec12480ce9ab3f6", + "0x2d6c57ca96d71f4d23b37bfcd63f70c8877afdcd20b9061372c5a9f4f5ec883b", + "0xd7d2d6c6b39a8bd276806810d77303e1fffbc2065f971462d3549b0841bc5159", + "0x72389b3e6dbb07c0cd4ec60f80d67ea24c5997e36457896c7047dba6bd388c0c", + "0xa683c35204e5d8378c5994a2b2cb66ef7cea7f72ce4a014f9de96e270d141f93", + "0xe73c7804f0a92e71739f049cc6d1f4296d6763694ce0f02222cce3a802b95e0c", + "0xe1d2a0db423aae642cf9e0d0a3a0e033ec287d72ad5d6775fb2ca0b754b5ccd1", + "0x2836a91b6f0a5da378fdcb358773b86fe4e2a497b5a8b0c6ea770f8168950cf5", + "0x207c070a56e477b151ac222d7d5582e4233a84f1760561b2c5ec26b6913b5be1", + "0xc6b82e66925ef9ac29b147459a0c7cf6810167b81d5a0d8119409bfde901e5fa", + "0x54f789a01b71afa8ea9a693866109c0fdbed99cbe60e976593a45ecaa2c51d86", + "0x67428f7da1b0ec6712491af063524e25bf846d1bcf1341fb5cd16de664e55253", + "0x9cb30239c73ca41a78aa23de7a5259e5c5320cb742d86e248e800cd4b7842f47", + "0x4ec478de462ba0bb5ee4f2bfcaafb9ee3fd65e2bcc18b39624db625d72ebbdb9", + "0xe9ba31abc5a0305325522cedfd3106cd0994a1be4462264ae630190ea3fd24e9", + "0xa293c713fa4280c0bc8febf0625bb7abe075f875e923cdeea554a48511eb1e57", + "0xa75d09ee8fa14ef49c5dbe05589f169ef70ee94963b7099fe61e4cc8b4c013ab", + "0xf0942330d11fdae546533e347054610329e3ea14b4f219d510f7e6535c9a3ee7", + "0x026d67e672413886db8a70f720b14cfdfb41adb82b7f439d5ba21b589b54e122", + "0xd49061710cb1c459d4a3386b2885cf835e9817d10381382ef8b4212c6d56d4a0", + "0xda04b4d64907968d284ca3072752af0cfc15ef3e5b866a22477bda9b1ad4bcf9", + "0xc3132e5a5899adf1daf3af4215cae7871cace74e8d876f450ebf0fdebe5372da", + "0x5305450d6a19385eec8c77fbf5be40e36551d7febcf1f7ebe6cabf3190e8fd76", + "0xbd7583e3d3d1af3619f54ae1824316f6d23a780647fb2b357db18076e602ae1b", + "0xcd515185622f58e69ad4f90618097d4c81177daad8719f046d3c14d6ce8375c3", + "0x5d518bf18a83f88660fdba41c8996b1e73dedd6cf5f0debd51703edb9e79296e", + "0xae7b3276b47d580f0275b8aeea0ed17f341a455275b5d8c321525a1258629dd5", + "0xf04a5b0379da532ef5a47e391f9db4a0224bd5de6661840ed9fea3b498bb3f69", + "0x617a8557906db76f6fbe284ef9df52298ecf840346589fc51f624b9686d5e218", + "0xc194d85f5a618d76aab6e49f5de491da2f602429dd772f364fe7fdfd3c6f0daf", + "0x6b138060c922aceeb0254e17c3a3548dd3e6904c61a67e058f961e080b3d9ad5", + "0x4de0534ace9c02822d3cf3d81d6dfbfcbd0964114503b56fba7db23b5f2befe9", + "0x06633e9de11b22610b6ef1fc1e8b952252099961f34d71789d8aee0e2f5785b6", + "0x8bfc843363eeb99dae04524455ae4df46ed6e6664edbb640c835305005dad194", + "0xf2f6f86a2e67a269cfdb62df43b9740b42a6eae3f52639acc93e9a8035194628", + "0x1584d00e4fa8d3f0e989f87002f9ff1851351044f99e6194fca52262da38f447", + "0xdc0801def9cf3897fd30f8200f0b6dc4966209e8879f26a7993154b135b4513d", + "0xb1b0fc31bfedf7bfbe445412e84ebdd76c3a09821a4436e72b95ffde019b955d", + "0x3c4e13e63d1bd76e1db089bec2acdc6fe39bbc6588bf4f4af117682aa8a7235f", + "0x2a6bb94e42fc21a26dff4db8004e9043a7a98d6d9583016cecad2baf37b4e882", + "0x7a0981e129e50d8abf6b5a8e0375b8631ca58ef7f9d39a455a4a4913b8951494", + "0x026e94b9b4b3fd407c2c39f8de81e0d0ecbdc0fc82c9ee8675e1a211ab24dc2f", + "0x60b20d6f87e1f3005df1b1bd5fb68ffe20f1fa54f21843ba5510c8169b917d72", + "0x8fa31f4aa8e666c9bd9d42395e08260525bf5ad2ff5719071ca84bdf6b671740", + "0x3e1e9d1a96c083b9e8c07e1ce163a13e5917db9731b6e29f51e2571fb4a8aadd", + "0x36645bfddd95120b6fbcdc9ad54ef1b1ffc8d0a3ab45851b692351ce2327d22d", + "0xc4589b30dc638fd5699e5e26300aac149843985846cec28590df72ab210c7145", + "0x6a050a8820f15c057644330083fa0b93deaf677a5569caeda70bc641051f9a38", + "0x9e2adb3df7bbb722d9ae61237166ba29ec84075f1e1c0b69077119222326803f", + "0x2e41470d47f517c606fe7cd40eb625fa20e07688e40cb1f9caddd80f27877547", + "0x248391e2db6d16b0d1fff77cfaafa82dd191e83ec7991c9a594a78b15b30183c", + "0x484e31e2b718d4803e71ce2223d1d24f1ea1e882ef0a01cc7704d6b306912e3b", + "0x0a14dc947165f37c01a8e225df6c0449347d987533f3aadcb25abe8066a31f5c", + "0x822ea0893ce74adf36d3138df4a6916e27d622abc1cb055ea8c82ddeb6f1b6c3", + "0x72ef874be609700ae58ae6706b6f4d6aadd5bd3fbfa5e017ac88125250ced8f0", + "0xf7e54a1ed7a15b3c5d99f09c5748d9e0944d93e88c7dbff78d33a9f0174f7856", + "0x5ae68f0dc97744f54a5e4aa402beaa4af17f8dedc81e5e98da566d802f6251df", + "0xa378c978831cf0853bdcd75e74218d0401eba6e8879a280d0f6552b7d4681c21", + "0x5229296eb8b7f594144a34635c87f1bf97f070249800af63827b8c3efc0d8426", + "0x3cecae54847f7732bd075cc4546c97d38963f633aeab68cac58a7d5a3177e6eb", + "0x7c14a4b4bd76b14f95a3cc9c046d609902090d7f15c694be9651ad437ac52683", + "0xb3f4b86defe35acb24a651602124c0595702de42fb3eed8d1366196e6c16402d", + "0xe50e7c9bf26de635ae23f434127b15f7640694988221b210969978a3fe8601e1", + "0xdd2fb9e00863ee8bd9e7f9b1f797aaa2127e66aa8d4cf6feeb7936fa3d46acbb", + "0xc543bb209f2682fbdefa8dacf51720954f37734dc3b1a34af1adfb6f0476bec1", + "0xd377c42d4e61fecde1ef150b4f2fd6fdbab18fc3c13bd6d3cda845e69516d3cb", + "0x622b34f8f3a6e6cdc035ae3359530551437c1e04aaa48c85d3da759b3afc7325", + "0x3e56eb277800889b4ca4a12a20eb33e17c78736636ed761882682ec91468de47", + "0x9c704ca0434b69c8c5731c139bb15f7fad0ac71b8c6863d5f85181e98d08a224", + "0x68499ddc9a08d967055b2e2faa974c93ec7da5d362912f7a164e8a022787f8bc", + "0xd2bb02824aba917a5ba22255e64f8576c72befd94daa60b7aad3065babbcebc8", + "0x8f6cb9bc8007964e4beca887943dc2ce64bd4bec91afb62d9d4449c5705c7086", + "0xd553a7e7d878b1747b87e6306b3a822167a945bc63ce650c57e194d8cd41beb2", + "0xc62ac1e135b490551bc99c70ca634bc3e47502b4be25d7a0e406cb26ba03ca18", + "0x4327232514afa77b8fe208a7a2094051b78b8c16b8eaf9da6dc30403c4c9896d", + "0x6b888ffa7a69d53fa6f70a9b083f7b49de19f680e29f838309aeec7f1821b00e", + "0xd3019c640f0413a1b787e99cbdf7f9d45f9a2c0afcce3288402cc797ce4f06f3", + "0x1baa3e5575457458859b2f2fc0f2bb09ae3f593787c62f1479e84842f69de91e", + "0xad1b8e08cf8cbf3d717e6766123974cf2450dcc0bf746133d71c2f15077eb8d1", + "0x817174986957d2d04f538fe5ca307d1f524a69c80cf97831002d1ddfe56bcb5f", + "0xaa2892eb967f4e588dfc6b6634e5b2aa88263f4b1e1e85998bea7f54f5015664", + "0x7bcaef11f9680e02b0008fc057819f06a1d117e318a43aee196dcdbc6b991dce", + "0xe56ad921fe297cdf000f04756cefabd75d309add549622cf3a7604d0d02d4d1f", + "0x988d447115c47d44f9d7abfdb380fe8927bce830a37b9be320d5154db934df2b", + "0x431001a2fc696bdc0e682f591434dde3344d994232191931c0a6e5d2e5498e90", + "0x6ed8c0e4597fca7c1c74ac524dedfb830714330b020dc1d1b2aea51bc311c4e4", + "0x8fb32fdb2670a4796df796379fb142e6ed6fa2e5f1fd51fb582a60befaf7df55", + "0xfcb88679cc8b85dbdb25dfc1b9f0a4602aa0d787e9e70ebd0a5728ff2e3b9572", + "0x00d82f45bf1088446a2706e923d8f43c800071c8f988239cfce7ad6a8c31b6dd", + "0xdf684190b2ce730df2b1cd78563dde0cab5539b30d0a06247d79f3aec73d617e", + "0xb0f93511970266249814b3073424d5ecf9b47edf7133009deacd317435940676", + "0x073ec7ff65ee722dcc829b69e80791a856a2200560f992316be6bdf497b5eac4", + "0x2d2a90d480df14e521db3dd380b470c29232e44e12838b54b51216a95659733c", + "0xd72e78f576726e546906e0e936baa1fc6625777edebf69da76c489b9ac057029", + "0x03ff5273dc2b190a1f52fa97e28945dda7b88c6d866551c9928a73d6ffb3f133", + "0xe23267e18401d49d5c2d130d91757d17d7dc074d055665fb33d4778559504f17", + "0x89fd9be047d599e128f9ab20b96fc506ddb8c30dd5a9b6782b05f18165f939a4", + "0x732b8ef837394ff6a580c3c21e72a72a2976fa7ee07a7152e0ba5101d5891f7c", + "0xe9999336ab64957ddaea70edad6e72a29b37bec1498deabc5d8341a93ee67197", + "0x49f60577477613545b37efb5317d9142d9f5d6d081908e1c061f6be452216b73", + "0x6fff08789f48c852189fcfb0327acce4e20281ae54c3b821790b2856c7ec1ff3", + "0x447f89ebe76365d8e308034e90241562bde8834dc1075eac89e28183b2a4769b", + "0x469eb409c46e96cd0e75878cde67974e319f00b0d336bdfedc098021ddc4f407", + "0xdd8365129aa6aa4ce914b5f1a917a573b7ee3a3ced4de5318859e8ea2d512de4", + "0x1ccda602493afe6ec11be1d523d3d76c7e79bb93aaa8dcb298e8bf3b9d5a13bd", + "0xfa3a4de9d5c620eb4d160ca7b49e86d99d109bf7866f2baa3d9c99346174dad9", + "0x7b9fb6a8d32f956fe50bec11e142cbfc320221a89e25ea1f579d76b4b2c9bf1a", + "0x01bacc1068711052a28f3e4f7e9fd80b996fe89a0a2bac647ecdc9d9b83e8b1e", + "0xd24d7c8b06dbf1db024c250d7ac4c8d2bfc8be39a0e8990de46b0ddd79aa4cec", + "0x422fd18a2b8735f348c092ca2bda2b69c676613b595f3906944f869e28070d23", + "0x938a51069120a3b4f3081fe083d9246d32b811d9f2e37a0fe6e1cef0e7ff4792", + "0xa9116e660bfc36372df2d1b87c309781403b48116c95f2f73669048bebddf81b", + "0x858728b26fcc100ae1a2bf373e2cac05537fc347790a9169710974efea7be830", + "0xe17c0bafaf4a96427becb15242bed29534970937cebbf3b6759bf470a9884b4e", + "0xe40f175756e1815e0d514af34a8da49518173c7f0b9fe1ef24b1a46d2aeb1ee9", + "0xc29562af361c92883d4ce12f04170e189eb62f473c2a24ccef7145a26e1de030", + "0xf8d3f3f63b252b8bd0a7ec90d5694c36cc1472c261bd45382bb485d11ede5a56", + "0xfcf7d3ab6cc4ea23882638e9de6531459ee27a3140ab491f756c723a52378ab4", + "0xe74cf40070946b562555a93e868313d46613164c5e23236851c8c35a7258947c", + "0xa79ccf4a86e8a34faa518079edabfc8884c52aa41571c45e05b171acb2c53f15", + "0xbcbc83dab8dbcf748bcad7f14c08340ca4f07318a09c9a32399f47dab5e60a47", + "0x51dd90b2af2f1766134ae9b2a82d885a4a194bce1213369ebf3e315d03ebaa73", + "0xcf18e42db84f9501927d969e9a7e4b42553473c63c3995d8e761b76f79c6d3f4", + "0x95d14b250f97721031748210fb7845f4ec78e05804a87e1ddd1269a220758643", + "0x1c73593545255b2bea59520ee29d30291b4731112d39f507ccb8c98252a8d39e", + "0xf6d582117cabecb8408ae4e9520369d72dbca7eaab8ce6842d2febb5a1b22354", + "0xa2c0b519f6b635f06e64f697948248e57eddcdc5919be502ec1a53e7501297b6", + "0xf2268cc03cba0244623a414fc7550f868157deef46a8c424bc1e6f4b70595975", + "0x16f40825d69784d4299039d7b361f075d1336f29b93947e57ae963f119de68aa", + "0x7d63f07c24dc55307dcfc3160433e7d382272ea411b931221ae30dcebc3068c2", + "0xe469c2b1e0a2370b5a9fd6db791e0ffc5bed870df922684706fc6dd425176c02", + "0x13c52a53e3d7d5524f163d70de3be5dc4a5c8bc95c64041aa21fbd14fc0bddb4", + "0x094a9ed475b0cf2c3d845cfbed5104adbb97b8e233c860470bd9c2f967fafc81", + "0x15f1e7ff285656c4b445af048f4c95224345c4879ebe4c753f3f7b3ba4e625c1", + "0x58128af04dd68678ec8a9cdc54183f495fea8353a89fb304ecb674d1312dbc2e", + "0x32081afac331a70b6174ec52c776de2ec27369136dee0a25b58985a4607a7935", + "0x7f48dc400b525c33b2ea17d47260032965a03b6d076ab4d02cebb7b571d82549", + "0x3321b772ad4ffbb3466734db9ab81e4aa749916ce8ae56349698dcc0834dfa1b", + "0x79846e34b081b22517d5514f26bd67190812501c430042b28e510d5fea386442", + "0x8fa6d5ca4cd3b175ba6fee940a941bd149b050c46a0661ac52774652fbef045d", + "0x506db869b12f2c0b153f314477ecaffa9b7c53df20d5b2dbcdcd073a3c76141b", + "0xe69339d7a63e25eac8dcdd9a70c621561bf32ccc1f89624e235819d39cfe5ca4", + "0xd506504ddbcfd1f91ec9d5d71fb5eb6c05566da7c4631f58cb99d20e7bc1d846", + "0xf20c78eb7a7560c9933d6a583ea23f403e6cb9176e0fc9807752633fce2bff7b", + "0x16392a1f31ba9f84eab7172705e01567853808f17748c87d3d2b112bd297696c", + "0xf55d43b56a371be8e174d3f617d662e7b45eb1326c8cc878409180cb516abb00", + "0xf9df7d04c863b1aa10e03c68dee16d0e17f7e514de9847a20a63affc6e1211ba", + "0xef814a0a1681df3972a6b20187ffbf58ca58f9fea6df0de32c1036723164d9fb", + "0xf8b49fffd0ffb70ff19c3c3781ab43e121f90c447d73ce242dd0a0e2bce56336", + "0xa6e89a02913d0e909104b67e7422339d3c93a564b97171db0260c3a8540be2ab", + "0x5216d9be46b9697a34181ca7b0d4627ad99311fe84c8fbf1da4e804bdd91d39e", + "0xd30d40124a55222fd69799407da38503c04f742b45f0127fc273be3fd5cb35ee", + "0xcf121f66227509e3829949153738f6cd1eba7210a4f4b960510a86d999cb9118", + "0x2e1f8a60fc9427a0325fd8150a8ea6fdcde91a13b40e999400c586c672798a49", + "0xb014af1c33675b75ff8c965b0f7f8eeb32320e42ba14db790a5c60896d774282", + "0xbc4867d1c344c81c33554abdf3384b544ee616f5909143ddb6b2bf4a0ba3d41d", + "0x0079e37734bb661b5bed93acfaa18179fda2617c4c37bedcee6654f79f0b2d9a", + "0x2d3cc57aff3730fcfb2e5552376cdff7cbd452842e5256dbe115aa3b1ad18dee", + "0x15288da0a82c6d0146bd29b8d31aa31b6f516f9997156ce91fdb367399e5b81d", + "0xb344ac87a84a52e90de58bab979ec4a2e5fd935836405880dd1151e5683085e4", + "0xde8156dd2e7abb20044a23844a06ddc1590c9ac6732923c21269dabc36bb246a", + "0x6580171454e664a67c320e63ffe7b950df9173af211983c730f40888db58643a", + "0x5ad58caa9bb6f379dc30ef06b339d724a91000acc92a15486c86b2271396bb3e", + "0x0479a5fdaf588b170bc2d8c11301a463448249c848a4f2ecd39cdd5f6fd94a09", + "0xed9335181e2fefe38459807029ae5614e98d1ed80fc5bcad68046d63fa7bd965", + "0xacef0d3fad5cc33c98daff6f5580a7e843c5445007b3edd4508372f221992901", + "0xa147d774480b83904768d78b654f01de94c50d54c2112900b23cc18df088c14e", + "0x620ce3590e2996f10089121597a6c05f2f8ccc94dde95fda12a194492f4a96a0", + "0x606815f9dcd9fc31a2485fd5bb82ab9c4e9edf31c7e31770e89365d643f32a71", + "0x0a8b6fc63adbc84796423326834fb9876a91b585300310964119019182d420b1", + "0xded997869b600c4fcc61119595da1b8574027d64f9c678f442e9f7a33ac90fef", + "0x448ee745b1cab75979a3ac3f99e941ec291c921af8f65f10cec4707fab2aa97c", + "0xa3c7a6580857935652f73fdfdffc5a315020a22a778e584e0b78eddd70a6f59c", + "0xd56555316bb971bcd4ed2dc26b53a667e0def14eec160230b3bc18cd03a3c65a", + "0x030f8abafc222277b1c9e39e2b75ca6c09afeda33517d8cb5fbba73af7e1e1de", + "0xce35fea1dbc514f8d540a72295e2457863d7ae2afb76ee3bf1cfae1a8ef91fcd", + "0x8d890590196c7af780fdd854989e2c776d472a6e04c82b4fc3c3bd3e30d01aa1", + "0x7fbdddf9f6d14fbd3e6217b184e9b43450ac3109da621c919a499db6575dd34a", + "0x41e0a2e9ca2781a6722b1ea5b302e1f01d1b34c80c6f494e248c030f375ba1ec", + "0x7991a805d4f1237190f5b28a55fa603dd5c7a7cf3325024bd2eaad981ca0d70b", + "0x27537dd0a5d7084451cedb82bbba192fc59dfb60bee59bfecb5f26c49019396f", + "0xd3e67cc97c90c09f19b528908d9a60fef68fde3335c041434960405210ccf860", + "0x68b51ea61f57127d3a75b46e44c97d588904bfb880f5774e21440b902c1b7831", + "0xce0b41fa28c47f7a420ea5efe6ec77e413f22b6f67e9fd8f6728ddd73e5c6baa", + "0x9e7b2c49c92a224a444f4f91221ac2173993decf40b21118ff5962dea3969fd1", + "0x6a611eaca85f609f1f79bfce34df97fb975f450e87e1f24d9a93b59ee0b0083f", + "0x70ec7e2b800c43d093f7b1dec60e6743244b4516394ff861e8fa9f265efdd28d" + ] + }, "accounts": { "0x0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, "0x0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, diff --git a/ethcore/res/ethereum/mcip3_test.json b/ethcore/res/ethereum/mcip3_test.json index d2e5ec8b6bceddde13ba10c7dba043e14c810548..391a625730a486fc28fc693e89120216c5622efa 100644 --- a/ethcore/res/ethereum/mcip3_test.json +++ b/ethcore/res/ethereum/mcip3_test.json @@ -1,168 +1,168 @@ { - "name":"MCIP3 Test", - "dataDir":"mcip3test", - "engine":{ - "Ethash":{ - "params":{ - "minimumDifficulty":"0x020000", - "difficultyBoundDivisor":"0x0800", - "durationLimit":"0x0d", - "homesteadTransition":"0x118c30", - "eip100bTransition":"0x7fffffffffffff", - "eip150Transition":"0x7fffffffffffff", - "eip160Transition":"0x7fffffffffffff", - "eip161abcTransition":"0x7fffffffffffff", - "eip161dTransition":"0x7fffffffffffff", - "eip649Transition":"0x7fffffffffffff", - "blockReward":"0x1105a0185b50a80000", - "mcip3Transition":"0x00", - "mcip3MinerReward":"0xd8d726b7177a80000", - "mcip3UbiReward":"0x2b5e3af16b1880000", - "mcip3UbiContract":"0x00efdd5883ec628983e9063c7d969fe268bbf310", - "mcip3DevReward":"0xc249fdd327780000", - "mcip3DevContract":"0x00756cf8159095948496617f5fb17ed95059f536" - } - } - }, - "params":{ - "gasLimitBoundDivisor":"0x0400", - "registrar":"0x5C271c4C9A67E7D73b7b3669d47504741354f21D", - "accountStartNonce":"0x00", - "maximumExtraDataSize":"0x20", - "minGasLimit":"0x1388", - "networkID":"0x76740b", - "forkBlock":"0x5b6", - "forkCanonHash":"0xa5e88ad9e34d113e264e307bc27e8471452c8fc13780324bb3abb96fd0558343", - "eip86Transition":"0x7fffffffffffff", - "eip98Transition":"0x7fffffffffffff", - "eip140Transition":"0x7fffffffffffff", - "eip155Transition":"0x7fffffffffffff", - "eip211Transition":"0x7fffffffffffff", - "eip214Transition":"0x7fffffffffffff", - "eip658Transition":"0x7fffffffffffff", - "maxCodeSize":"0x6000", - "maxCodeSizeTransition": "0x7fffffffffffff" - }, - "genesis":{ - "seal":{ - "ethereum":{ - "nonce":"0x000000000000002a", - "mixHash":"0x00000000000000000000000000000000000000647572616c65787365646c6578" - } - }, - "difficulty":"0x3d0900", - "author":"0x0000000000000000000000000000000000000000", - "timestamp":"0x00", - "parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000", - "extraData":"", - "gasLimit":"0x7a1200" - }, - "nodes":[ - "enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@52.16.188.185:30303", - "enode://3f1d12044546b76342d59d4a05532c14b85aa669704bfe1f864fe079415aa2c02d743e03218e57a33fb94523adb54032871a6c51b2cc5514cb7c7e35b3ed0a99@13.93.211.84:30303", - "enode://78de8a0916848093c73790ead81d1928bec737d565119932b98c6b100d944b7a95e94f847f689fc723399d2e31129d182f7ef3863f2b4c820abbf3ab2722344d@191.235.84.50:30303", - "enode://158f8aab45f6d19c6cbf4a089c2670541a8da11978a2f90dbf6a502a4a3bab80d288afdbeb7ec0ef6d92de563767f3b1ea9e8e334ca711e9f8e2df5a0385e8e6@13.75.154.138:30303", - "enode://1118980bf48b0a3640bdba04e0fe78b1add18e1cd99bf22d53daac1fd9972ad650df52176e7c7d89d1114cfef2bc23a2959aa54998a46afcf7d91809f0855082@52.74.57.123:30303", - "enode://979b7fa28feeb35a4741660a16076f1943202cb72b6af70d327f053e248bab9ba81760f39d0701ef1d8f89cc1fbd2cacba0710a12cd5314d5e0c9021aa3637f9@5.1.83.226:30303", - "enode://d302f52c8789ad87ee528f1431a67f1aa646c9bec17babb4665dfb3d61de5b9119a70aa77b2147a5f28854092ba09769323c1c552a6ac6f6a34cbcf767e2d2fe@158.69.248.48:30303", - "enode://c72564bce8331ae298fb8ece113a456e3927d7e5989c2be3e445678b3600579f722410ef9bbfe339335d676af77343cb21b5b1703b7bebc32be85fce937a2220@191.252.185.71:30303", - "enode://e3ae4d25ee64791ff98bf17c37acf90933359f2505c00f65c84f6863231a32a94153cadb0a462e428f18f35ded6bd91cd91033d26576a28558c22678be9cfaee@5.63.158.137:35555" - ], - "accounts":{ - "0000000000000000000000000000000000000001":{ - "balance":"1", - "builtin":{ - "name":"ecrecover", - "pricing":{ - "linear":{ - "base":3000, - "word":0 - } - } - } - }, - "0000000000000000000000000000000000000002":{ - "balance":"1", - "builtin":{ - "name":"sha256", - "pricing":{ - "linear":{ - "base":60, - "word":12 - } - } - } - }, - "0000000000000000000000000000000000000003":{ - "balance":"1", - "builtin":{ - "name":"ripemd160", - "pricing":{ - "linear":{ - "base":600, - "word":120 - } - } - } - }, - "0000000000000000000000000000000000000004":{ - "balance":"1", - "builtin":{ - "name":"identity", - "pricing":{ - "linear":{ - "base":15, - "word":3 - } - } - } - }, - "0000000000000000000000000000000000000005":{ - "builtin":{ - "name":"modexp", - "activate_at":"0x7fffffffffffff", - "pricing":{ - "modexp":{ - "divisor":20 - } - } - } - }, - "0000000000000000000000000000000000000006":{ - "builtin":{ - "name":"alt_bn128_add", - "activate_at":"0x7fffffffffffff", - "pricing":{ - "linear":{ - "base":500, - "word":0 - } - } - } - }, - "0000000000000000000000000000000000000007":{ - "builtin":{ - "name":"alt_bn128_mul", - "activate_at":"0x7fffffffffffff", - "pricing":{ - "linear":{ - "base":40000, - "word":0 - } - } - } - }, - "0000000000000000000000000000000000000008":{ - "builtin":{ - "name":"alt_bn128_pairing", - "activate_at":"0x7fffffffffffff", - "pricing":{ - "alt_bn128_pairing":{ - "base":100000, - "pair":80000 - } - } - } - } - } + "name":"MCIP3 Test", + "dataDir":"mcip3test", + "engine":{ + "Ethash":{ + "params":{ + "minimumDifficulty":"0x020000", + "difficultyBoundDivisor":"0x0800", + "durationLimit":"0x0d", + "homesteadTransition":"0x118c30", + "eip100bTransition":"0x7fffffffffffff", + "eip649Transition":"0x7fffffffffffff", + "blockReward":"0x1105a0185b50a80000", + "mcip3Transition":"0x00", + "mcip3MinerReward":"0xd8d726b7177a80000", + "mcip3UbiReward":"0x2b5e3af16b1880000", + "mcip3UbiContract":"0x00efdd5883ec628983e9063c7d969fe268bbf310", + "mcip3DevReward":"0xc249fdd327780000", + "mcip3DevContract":"0x00756cf8159095948496617f5fb17ed95059f536" + } + } + }, + "params":{ + "gasLimitBoundDivisor":"0x0400", + "registrar":"0x5C271c4C9A67E7D73b7b3669d47504741354f21D", + "accountStartNonce":"0x00", + "maximumExtraDataSize":"0x20", + "minGasLimit":"0x1388", + "networkID":"0x76740b", + "forkBlock":"0x5b6", + "forkCanonHash":"0xa5e88ad9e34d113e264e307bc27e8471452c8fc13780324bb3abb96fd0558343", + "eip150Transition":"0x7fffffffffffff", + "eip160Transition":"0x7fffffffffffff", + "eip161abcTransition":"0x7fffffffffffff", + "eip161dTransition":"0x7fffffffffffff", + "eip86Transition":"0x7fffffffffffff", + "eip98Transition":"0x7fffffffffffff", + "eip140Transition":"0x7fffffffffffff", + "eip155Transition":"0x7fffffffffffff", + "eip211Transition":"0x7fffffffffffff", + "eip214Transition":"0x7fffffffffffff", + "eip658Transition":"0x7fffffffffffff", + "maxCodeSize":"0x6000", + "maxCodeSizeTransition": "0x7fffffffffffff" + }, + "genesis":{ + "seal":{ + "ethereum":{ + "nonce":"0x000000000000002a", + "mixHash":"0x00000000000000000000000000000000000000647572616c65787365646c6578" + } + }, + "difficulty":"0x3d0900", + "author":"0x0000000000000000000000000000000000000000", + "timestamp":"0x00", + "parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData":"", + "gasLimit":"0x7a1200" + }, + "nodes":[ + "enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@52.16.188.185:30303", + "enode://3f1d12044546b76342d59d4a05532c14b85aa669704bfe1f864fe079415aa2c02d743e03218e57a33fb94523adb54032871a6c51b2cc5514cb7c7e35b3ed0a99@13.93.211.84:30303", + "enode://78de8a0916848093c73790ead81d1928bec737d565119932b98c6b100d944b7a95e94f847f689fc723399d2e31129d182f7ef3863f2b4c820abbf3ab2722344d@191.235.84.50:30303", + "enode://158f8aab45f6d19c6cbf4a089c2670541a8da11978a2f90dbf6a502a4a3bab80d288afdbeb7ec0ef6d92de563767f3b1ea9e8e334ca711e9f8e2df5a0385e8e6@13.75.154.138:30303", + "enode://1118980bf48b0a3640bdba04e0fe78b1add18e1cd99bf22d53daac1fd9972ad650df52176e7c7d89d1114cfef2bc23a2959aa54998a46afcf7d91809f0855082@52.74.57.123:30303", + "enode://979b7fa28feeb35a4741660a16076f1943202cb72b6af70d327f053e248bab9ba81760f39d0701ef1d8f89cc1fbd2cacba0710a12cd5314d5e0c9021aa3637f9@5.1.83.226:30303", + "enode://d302f52c8789ad87ee528f1431a67f1aa646c9bec17babb4665dfb3d61de5b9119a70aa77b2147a5f28854092ba09769323c1c552a6ac6f6a34cbcf767e2d2fe@158.69.248.48:30303", + "enode://c72564bce8331ae298fb8ece113a456e3927d7e5989c2be3e445678b3600579f722410ef9bbfe339335d676af77343cb21b5b1703b7bebc32be85fce937a2220@191.252.185.71:30303", + "enode://e3ae4d25ee64791ff98bf17c37acf90933359f2505c00f65c84f6863231a32a94153cadb0a462e428f18f35ded6bd91cd91033d26576a28558c22678be9cfaee@5.63.158.137:35555" + ], + "accounts":{ + "0000000000000000000000000000000000000001":{ + "balance":"1", + "builtin":{ + "name":"ecrecover", + "pricing":{ + "linear":{ + "base":3000, + "word":0 + } + } + } + }, + "0000000000000000000000000000000000000002":{ + "balance":"1", + "builtin":{ + "name":"sha256", + "pricing":{ + "linear":{ + "base":60, + "word":12 + } + } + } + }, + "0000000000000000000000000000000000000003":{ + "balance":"1", + "builtin":{ + "name":"ripemd160", + "pricing":{ + "linear":{ + "base":600, + "word":120 + } + } + } + }, + "0000000000000000000000000000000000000004":{ + "balance":"1", + "builtin":{ + "name":"identity", + "pricing":{ + "linear":{ + "base":15, + "word":3 + } + } + } + }, + "0000000000000000000000000000000000000005":{ + "builtin":{ + "name":"modexp", + "activate_at":"0x7fffffffffffff", + "pricing":{ + "modexp":{ + "divisor":20 + } + } + } + }, + "0000000000000000000000000000000000000006":{ + "builtin":{ + "name":"alt_bn128_add", + "activate_at":"0x7fffffffffffff", + "pricing":{ + "linear":{ + "base":500, + "word":0 + } + } + } + }, + "0000000000000000000000000000000000000007":{ + "builtin":{ + "name":"alt_bn128_mul", + "activate_at":"0x7fffffffffffff", + "pricing":{ + "linear":{ + "base":40000, + "word":0 + } + } + } + }, + "0000000000000000000000000000000000000008":{ + "builtin":{ + "name":"alt_bn128_pairing", + "activate_at":"0x7fffffffffffff", + "pricing":{ + "alt_bn128_pairing":{ + "base":100000, + "pair":80000 + } + } + } + } + } } diff --git a/ethcore/res/ethereum/mcip6_byz.json b/ethcore/res/ethereum/mcip6_byz.json index 8028e6b1bbbb52370bb7a2c765a3697c4fc08af6..a12df7c71156a3056e015e28b40a77c6c278c453 100644 --- a/ethcore/res/ethereum/mcip6_byz.json +++ b/ethcore/res/ethereum/mcip6_byz.json @@ -1,161 +1,161 @@ { - "name":"Musicoin Byzantium Test", - "dataDir":"mcip6test", - "engine":{ - "Ethash":{ - "params":{ - "minimumDifficulty":"0x020000", - "difficultyBoundDivisor":"0x0800", - "durationLimit":"0x0d", - "homesteadTransition":"0x17", - "eip100bTransition":"0x2a", - "eip150Transition":"0x2a", - "eip160Transition":"0x7fffffffffffff", - "eip161abcTransition":"0x7fffffffffffff", - "eip161dTransition":"0x7fffffffffffff", - "eip649Transition":"0x2a", - "blockReward":"0x1105a0185b50a80000", - "mcip3Transition":"0x17", - "mcip3MinerReward":"0xd8d726b7177a80000", - "mcip3UbiReward":"0x2b5e3af16b1880000", - "mcip3UbiContract":"0x00efdd5883ec628983e9063c7d969fe268bbf310", - "mcip3DevReward":"0xc249fdd327780000", - "mcip3DevContract":"0x00756cf8159095948496617f5fb17ed95059f536" - } - } - }, - "params":{ - "gasLimitBoundDivisor":"0x0400", - "registrar":"0x5C271c4C9A67E7D73b7b3669d47504741354f21D", - "accountStartNonce":"0x00", - "maximumExtraDataSize":"0x20", - "minGasLimit":"0x1388", - "networkID":"0x76740c", - "forkBlock":"0x2b", - "forkCanonHash":"0x23c3171e864a5d513a3ef85e4cf86dac4cc36b89e5b8e63bf0ebcca68b9e43c9", - "eip86Transition":"0x7fffffffffffff", - "eip98Transition":"0x7fffffffffffff", - "eip140Transition":"0x2a", - "eip155Transition":"0x2a", - "eip211Transition":"0x2a", - "eip214Transition":"0x2a", - "eip658Transition":"0x2a", - "maxCodeSize":"0x6000", - "maxCodeSizeTransition": "0x7fffffffffffff" - }, - "genesis":{ - "seal":{ - "ethereum":{ - "nonce":"0x000000000000002a", - "mixHash":"0x00000000000000000000000000000000000000647572616c65787365646c6578" - } - }, - "difficulty":"0x3d0900", - "author":"0x0000000000000000000000000000000000000000", - "timestamp":"0x00", - "parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000", - "extraData":"", - "gasLimit":"0x7a1200" - }, - "nodes":[ - "enode://5ddc110733f6d34101973cdef3f9b43484159acf6f816d3b1ee92bc3c98ea453e857bb1207edf0ec0242008ab3a0f9f05eeaee99d47bd414c08a5bdf4847de13@176.9.3.148:30303", - "enode://38f074f4db8e64dfbaf87984bf290eef67772a901a7113d1b62f36216be152b8450c393d6fc562a5e38f04f99bc8f439a99010a230b1d92dc1df43bf0bd00615@176.9.3.148:30403" - ], - "accounts":{ - "0000000000000000000000000000000000000001":{ - "balance":"1", - "builtin":{ - "name":"ecrecover", - "pricing":{ - "linear":{ - "base":3000, - "word":0 - } - } - } - }, - "0000000000000000000000000000000000000002":{ - "balance":"1", - "builtin":{ - "name":"sha256", - "pricing":{ - "linear":{ - "base":60, - "word":12 - } - } - } - }, - "0000000000000000000000000000000000000003":{ - "balance":"1", - "builtin":{ - "name":"ripemd160", - "pricing":{ - "linear":{ - "base":600, - "word":120 - } - } - } - }, - "0000000000000000000000000000000000000004":{ - "balance":"1", - "builtin":{ - "name":"identity", - "pricing":{ - "linear":{ - "base":15, - "word":3 - } - } - } - }, - "0000000000000000000000000000000000000005":{ - "builtin":{ - "name":"modexp", - "activate_at":"0x2a", - "pricing":{ - "modexp":{ - "divisor":20 - } - } - } - }, - "0000000000000000000000000000000000000006":{ - "builtin":{ - "name":"alt_bn128_add", - "activate_at":"0x2a", - "pricing":{ - "linear":{ - "base":500, - "word":0 - } - } - } - }, - "0000000000000000000000000000000000000007":{ - "builtin":{ - "name":"alt_bn128_mul", - "activate_at":"0x2a", - "pricing":{ - "linear":{ - "base":40000, - "word":0 - } - } - } - }, - "0000000000000000000000000000000000000008":{ - "builtin":{ - "name":"alt_bn128_pairing", - "activate_at":"0x2a", - "pricing":{ - "alt_bn128_pairing":{ - "base":100000, - "pair":80000 - } - } - } - } - } + "name":"Musicoin Byzantium Test", + "dataDir":"mcip6test", + "engine":{ + "Ethash":{ + "params":{ + "minimumDifficulty":"0x020000", + "difficultyBoundDivisor":"0x0800", + "durationLimit":"0x0d", + "homesteadTransition":"0x17", + "eip100bTransition":"0x2a", + "eip649Transition":"0x2a", + "blockReward":"0x1105a0185b50a80000", + "mcip3Transition":"0x17", + "mcip3MinerReward":"0xd8d726b7177a80000", + "mcip3UbiReward":"0x2b5e3af16b1880000", + "mcip3UbiContract":"0x00efdd5883ec628983e9063c7d969fe268bbf310", + "mcip3DevReward":"0xc249fdd327780000", + "mcip3DevContract":"0x00756cf8159095948496617f5fb17ed95059f536" + } + } + }, + "params":{ + "gasLimitBoundDivisor":"0x0400", + "registrar":"0x5C271c4C9A67E7D73b7b3669d47504741354f21D", + "accountStartNonce":"0x00", + "maximumExtraDataSize":"0x20", + "minGasLimit":"0x1388", + "networkID":"0x76740c", + "forkBlock":"0x2b", + "forkCanonHash":"0x23c3171e864a5d513a3ef85e4cf86dac4cc36b89e5b8e63bf0ebcca68b9e43c9", + "eip150Transition":"0x2a", + "eip160Transition":"0x7fffffffffffff", + "eip161abcTransition":"0x7fffffffffffff", + "eip161dTransition":"0x7fffffffffffff", + "eip86Transition":"0x7fffffffffffff", + "eip98Transition":"0x7fffffffffffff", + "eip140Transition":"0x2a", + "eip155Transition":"0x2a", + "eip211Transition":"0x2a", + "eip214Transition":"0x2a", + "eip658Transition":"0x2a", + "maxCodeSize":"0x6000", + "maxCodeSizeTransition": "0x7fffffffffffff" + }, + "genesis":{ + "seal":{ + "ethereum":{ + "nonce":"0x000000000000002a", + "mixHash":"0x00000000000000000000000000000000000000647572616c65787365646c6578" + } + }, + "difficulty":"0x3d0900", + "author":"0x0000000000000000000000000000000000000000", + "timestamp":"0x00", + "parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData":"", + "gasLimit":"0x7a1200" + }, + "nodes":[ + "enode://5ddc110733f6d34101973cdef3f9b43484159acf6f816d3b1ee92bc3c98ea453e857bb1207edf0ec0242008ab3a0f9f05eeaee99d47bd414c08a5bdf4847de13@176.9.3.148:30303", + "enode://38f074f4db8e64dfbaf87984bf290eef67772a901a7113d1b62f36216be152b8450c393d6fc562a5e38f04f99bc8f439a99010a230b1d92dc1df43bf0bd00615@176.9.3.148:30403" + ], + "accounts":{ + "0000000000000000000000000000000000000001":{ + "balance":"1", + "builtin":{ + "name":"ecrecover", + "pricing":{ + "linear":{ + "base":3000, + "word":0 + } + } + } + }, + "0000000000000000000000000000000000000002":{ + "balance":"1", + "builtin":{ + "name":"sha256", + "pricing":{ + "linear":{ + "base":60, + "word":12 + } + } + } + }, + "0000000000000000000000000000000000000003":{ + "balance":"1", + "builtin":{ + "name":"ripemd160", + "pricing":{ + "linear":{ + "base":600, + "word":120 + } + } + } + }, + "0000000000000000000000000000000000000004":{ + "balance":"1", + "builtin":{ + "name":"identity", + "pricing":{ + "linear":{ + "base":15, + "word":3 + } + } + } + }, + "0000000000000000000000000000000000000005":{ + "builtin":{ + "name":"modexp", + "activate_at":"0x2a", + "pricing":{ + "modexp":{ + "divisor":20 + } + } + } + }, + "0000000000000000000000000000000000000006":{ + "builtin":{ + "name":"alt_bn128_add", + "activate_at":"0x2a", + "pricing":{ + "linear":{ + "base":500, + "word":0 + } + } + } + }, + "0000000000000000000000000000000000000007":{ + "builtin":{ + "name":"alt_bn128_mul", + "activate_at":"0x2a", + "pricing":{ + "linear":{ + "base":40000, + "word":0 + } + } + } + }, + "0000000000000000000000000000000000000008":{ + "builtin":{ + "name":"alt_bn128_pairing", + "activate_at":"0x2a", + "pricing":{ + "alt_bn128_pairing":{ + "base":100000, + "pair":80000 + } + } + } + } + } } diff --git a/ethcore/res/ethereum/morden.json b/ethcore/res/ethereum/morden.json index 04c5a1244418101943ed94a07cccf898d410334d..b61799c0c9d8c73c01b3eb3af57d2bd9aa7a014a 100644 --- a/ethcore/res/ethereum/morden.json +++ b/ethcore/res/ethereum/morden.json @@ -1,6 +1,6 @@ { "name": "Morden", - "dataDir": "test", + "dataDir": "morden", "engine": { "Ethash": { "params": { @@ -9,13 +9,9 @@ "durationLimit": "0x0d", "blockReward": "0x4563918244F40000", "homesteadTransition": 494000, - "eip150Transition": 1783000, - "eip160Transition": 1915000, "ecip1010PauseTransition": 1915000, "ecip1010ContinueTransition": 3415000, "ecip1017EraRounds": 2000000, - "eip161abcTransition": "0x7fffffffffffffff", - "eip161dTransition": "0x7fffffffffffffff", "bombDefuseTransition": 2300000 } } @@ -30,6 +26,10 @@ "chainID": "0x3e", "forkBlock": "0x1b34d8", "forkCanonHash": "0xf376243aeff1f256d970714c3de9fd78fa4e63cf63e32a51fe1169e375d98145", + "eip150Transition": 1783000, + "eip160Transition": 1915000, + "eip161abcTransition": "0x7fffffffffffffff", + "eip161dTransition": "0x7fffffffffffffff", "eip155Transition": 1915000, "eip98Transition": "0x7fffffffffffff", "eip86Transition": "0x7fffffffffffff" diff --git a/ethcore/res/ethereum/musicoin.json b/ethcore/res/ethereum/musicoin.json index efe81c58821e9c050353dfd1ff1347f71dda3503..724f11478bf1186429d1a73045282bc21a24c9d5 100644 --- a/ethcore/res/ethereum/musicoin.json +++ b/ethcore/res/ethereum/musicoin.json @@ -1,165 +1,164 @@ { - "name":"Musicoin", - "dataDir":"musicoin", - "engine":{ - "Ethash":{ - "params":{ - "minimumDifficulty":"0x020000", - "difficultyBoundDivisor":"0x0800", - "durationLimit":"0x0d", - "homesteadTransition":"0x118c30", - "eip100bTransition":"0x21e88e", - "eip150Transition":"0x21e88e", - "eip160Transition":"0x7fffffffffffff", - "eip161abcTransition":"0x7fffffffffffff", - "eip161dTransition":"0x7fffffffffffff", - "eip649Transition":"0x21e88e", - "blockReward":"0x1105a0185b50a80000", - "mcip3Transition":"0x124f81", - "mcip3MinerReward":"0xd8d726b7177a80000", - "mcip3UbiReward":"0x2b5e3af16b1880000", - "mcip3UbiContract":"0x00efdd5883ec628983e9063c7d969fe268bbf310", - "mcip3DevReward":"0xc249fdd327780000", - "mcip3DevContract":"0x00756cf8159095948496617f5fb17ed95059f536" - } - } - }, - "params":{ - "gasLimitBoundDivisor":"0x0400", - "registrar":"0x5C271c4C9A67E7D73b7b3669d47504741354f21D", - "accountStartNonce":"0x00", - "maximumExtraDataSize":"0x20", - "minGasLimit":"0x1388", - "networkID":"0x76740f", - "forkBlock":"0x1d8015", - "forkCanonHash":"0x380602acf82b629a0be6b5adb2b4a801e960a07dc8261bf196d21befdbb8f2f9", - "eip86Transition":"0x7fffffffffffff", - "eip98Transition":"0x7fffffffffffff", - "eip140Transition":"0x21e88e", - "eip155Transition":"0x21e88e", - "eip211Transition":"0x21e88e", - "eip214Transition":"0x21e88e", - "eip658Transition":"0x21e88e", - "maxCodeSize":"0x6000", - "maxCodeSizeTransition": "0x7fffffffffffff" - }, - "genesis":{ - "seal":{ - "ethereum":{ - "nonce":"0x000000000000002a", - "mixHash":"0x00000000000000000000000000000000000000647572616c65787365646c6578" - } - }, - "difficulty":"0x3d0900", - "author":"0x0000000000000000000000000000000000000000", - "timestamp":"0x00", - "parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000", - "extraData":"", - "gasLimit":"0x7a1200" - }, - "nodes":[ - "enode://09fcd36d553044c8b499b9b9e13a228ffd99572c513f77073d41f009717c464cd4399c0e665d6aff1590324254ee4e698b2b2533b1998dd04d896b9d6aff7895@35.185.67.35:30303", - "enode://89e51a34770a0badf8ea18c4c4d2c361cde707abd60031d99b1ab3010363e1898230a516ddb37d974af8d8db1b322779d7fe0caae0617bed4924d1b4968cf92b@35.231.48.142:30303", - "enode://b58c0c71f08864c0cf7fa9dea2c4cbefae5ae7a36cc30d286603b24982d25f3ccc056b589119324c51768fc2054b8c529ecf682e06e1e9980170b93ff194ed7a@132.148.132.9:30303", - "enode://d302f52c8789ad87ee528f1431a67f1aa646c9bec17babb4665dfb3d61de5b9119a70aa77b2147a5f28854092ba09769323c1c552a6ac6f6a34cbcf767e2d2fe@158.69.248.48:30303", - "enode://c72564bce8331ae298fb8ece113a456e3927d7e5989c2be3e445678b3600579f722410ef9bbfe339335d676af77343cb21b5b1703b7bebc32be85fce937a2220@191.252.185.71:30303", - "enode://e3ae4d25ee64791ff98bf17c37acf90933359f2505c00f65c84f6863231a32a94153cadb0a462e428f18f35ded6bd91cd91033d26576a28558c22678be9cfaee@5.63.158.137:35555" - ], - "accounts":{ - "0000000000000000000000000000000000000001":{ - "balance":"1", - "builtin":{ - "name":"ecrecover", - "pricing":{ - "linear":{ - "base":3000, - "word":0 - } - } - } - }, - "0000000000000000000000000000000000000002":{ - "balance":"1", - "builtin":{ - "name":"sha256", - "pricing":{ - "linear":{ - "base":60, - "word":12 - } - } - } - }, - "0000000000000000000000000000000000000003":{ - "balance":"1", - "builtin":{ - "name":"ripemd160", - "pricing":{ - "linear":{ - "base":600, - "word":120 - } - } - } - }, - "0000000000000000000000000000000000000004":{ - "balance":"1", - "builtin":{ - "name":"identity", - "pricing":{ - "linear":{ - "base":15, - "word":3 - } - } - } - }, - "0000000000000000000000000000000000000005":{ - "builtin":{ - "name":"modexp", - "activate_at":"0x21e88e", - "pricing":{ - "modexp":{ - "divisor":20 - } - } - } - }, - "0000000000000000000000000000000000000006":{ - "builtin":{ - "name":"alt_bn128_add", - "activate_at":"0x21e88e", - "pricing":{ - "linear":{ - "base":500, - "word":0 - } - } - } - }, - "0000000000000000000000000000000000000007":{ - "builtin":{ - "name":"alt_bn128_mul", - "activate_at":"0x21e88e", - "pricing":{ - "linear":{ - "base":40000, - "word":0 - } - } - } - }, - "0000000000000000000000000000000000000008":{ - "builtin":{ - "name":"alt_bn128_pairing", - "activate_at":"0x21e88e", - "pricing":{ - "alt_bn128_pairing":{ - "base":100000, - "pair":80000 - } - } - } - } - } + "name":"Musicoin", + "dataDir":"musicoin", + "engine":{ + "Ethash":{ + "params":{ + "minimumDifficulty":"0x020000", + "difficultyBoundDivisor":"0x0800", + "durationLimit":"0x0d", + "homesteadTransition":"0x118c30", + "eip100bTransition":"0x21e88e", + "eip649Transition":"0x21e88e", + "blockReward":"0x1105a0185b50a80000", + "mcip3Transition":"0x124f81", + "mcip3MinerReward":"0xd8d726b7177a80000", + "mcip3UbiReward":"0x2b5e3af16b1880000", + "mcip3UbiContract":"0x00efdd5883ec628983e9063c7d969fe268bbf310", + "mcip3DevReward":"0xc249fdd327780000", + "mcip3DevContract":"0x00756cf8159095948496617f5fb17ed95059f536" + } + } + }, + "params":{ + "gasLimitBoundDivisor":"0x0400", + "registrar":"0x5C271c4C9A67E7D73b7b3669d47504741354f21D", + "accountStartNonce":"0x00", + "maximumExtraDataSize":"0x20", + "minGasLimit":"0x1388", + "networkID":"0x76740f", + "forkBlock":"0x1d8015", + "forkCanonHash":"0x380602acf82b629a0be6b5adb2b4a801e960a07dc8261bf196d21befdbb8f2f9", + "eip150Transition":"0x21e88e", + "eip160Transition":"0x21e88e", + "eip161abcTransition":"0x21e88e", + "eip161dTransition":"0x21e88e", + "eip86Transition":"0x7fffffffffffff", + "eip98Transition":"0x7fffffffffffff", + "eip140Transition":"0x21e88e", + "eip155Transition":"0x21e88e", + "eip211Transition":"0x21e88e", + "eip214Transition":"0x21e88e", + "eip658Transition":"0x21e88e", + "maxCodeSize":"0x6000" + }, + "genesis":{ + "seal":{ + "ethereum":{ + "nonce":"0x000000000000002a", + "mixHash":"0x00000000000000000000000000000000000000647572616c65787365646c6578" + } + }, + "difficulty":"0x3d0900", + "author":"0x0000000000000000000000000000000000000000", + "timestamp":"0x00", + "parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData":"", + "gasLimit":"0x7a1200" + }, + "nodes":[ + "enode://09fcd36d553044c8b499b9b9e13a228ffd99572c513f77073d41f009717c464cd4399c0e665d6aff1590324254ee4e698b2b2533b1998dd04d896b9d6aff7895@35.185.67.35:30303", + "enode://89e51a34770a0badf8ea18c4c4d2c361cde707abd60031d99b1ab3010363e1898230a516ddb37d974af8d8db1b322779d7fe0caae0617bed4924d1b4968cf92b@35.231.48.142:30303", + "enode://b58c0c71f08864c0cf7fa9dea2c4cbefae5ae7a36cc30d286603b24982d25f3ccc056b589119324c51768fc2054b8c529ecf682e06e1e9980170b93ff194ed7a@132.148.132.9:30303", + "enode://d302f52c8789ad87ee528f1431a67f1aa646c9bec17babb4665dfb3d61de5b9119a70aa77b2147a5f28854092ba09769323c1c552a6ac6f6a34cbcf767e2d2fe@158.69.248.48:30303", + "enode://c72564bce8331ae298fb8ece113a456e3927d7e5989c2be3e445678b3600579f722410ef9bbfe339335d676af77343cb21b5b1703b7bebc32be85fce937a2220@191.252.185.71:30303", + "enode://e3ae4d25ee64791ff98bf17c37acf90933359f2505c00f65c84f6863231a32a94153cadb0a462e428f18f35ded6bd91cd91033d26576a28558c22678be9cfaee@5.63.158.137:35555" + ], + "accounts":{ + "0000000000000000000000000000000000000001":{ + "balance":"1", + "builtin":{ + "name":"ecrecover", + "pricing":{ + "linear":{ + "base":3000, + "word":0 + } + } + } + }, + "0000000000000000000000000000000000000002":{ + "balance":"1", + "builtin":{ + "name":"sha256", + "pricing":{ + "linear":{ + "base":60, + "word":12 + } + } + } + }, + "0000000000000000000000000000000000000003":{ + "balance":"1", + "builtin":{ + "name":"ripemd160", + "pricing":{ + "linear":{ + "base":600, + "word":120 + } + } + } + }, + "0000000000000000000000000000000000000004":{ + "balance":"1", + "builtin":{ + "name":"identity", + "pricing":{ + "linear":{ + "base":15, + "word":3 + } + } + } + }, + "0000000000000000000000000000000000000005":{ + "builtin":{ + "name":"modexp", + "activate_at":"0x21e88e", + "pricing":{ + "modexp":{ + "divisor":20 + } + } + } + }, + "0000000000000000000000000000000000000006":{ + "builtin":{ + "name":"alt_bn128_add", + "activate_at":"0x21e88e", + "pricing":{ + "linear":{ + "base":500, + "word":0 + } + } + } + }, + "0000000000000000000000000000000000000007":{ + "builtin":{ + "name":"alt_bn128_mul", + "activate_at":"0x21e88e", + "pricing":{ + "linear":{ + "base":40000, + "word":0 + } + } + } + }, + "0000000000000000000000000000000000000008":{ + "builtin":{ + "name":"alt_bn128_pairing", + "activate_at":"0x21e88e", + "pricing":{ + "alt_bn128_pairing":{ + "base":100000, + "pair":80000 + } + } + } + } + } } diff --git a/ethcore/res/ethereum/olympic.json b/ethcore/res/ethereum/olympic.json index 6854f2b78a94ac33c6dc207dd3dac87f72253afb..3ae9baddf2ba161d25c8d1062bec4bc316182e26 100644 --- a/ethcore/res/ethereum/olympic.json +++ b/ethcore/res/ethereum/olympic.json @@ -7,11 +7,7 @@ "difficultyBoundDivisor": "0x0800", "durationLimit": "0x08", "blockReward": "0x14D1120D7B160000", - "homesteadTransition": "0x7fffffffffffffff", - "eip150Transition": "0x7fffffffffffffff", - "eip160Transition": "0x7fffffffffffffff", - "eip161abcTransition": "0x7fffffffffffffff", - "eip161dTransition": "0x7fffffffffffffff" + "homesteadTransition": "0x7fffffffffffffff" } } }, @@ -22,6 +18,10 @@ "maximumExtraDataSize": "0x0400", "minGasLimit": "125000", "networkID" : "0x0", + "eip150Transition": "0x7fffffffffffffff", + "eip160Transition": "0x7fffffffffffffff", + "eip161abcTransition": "0x7fffffffffffffff", + "eip161dTransition": "0x7fffffffffffffff", "eip98Transition": "0x7fffffffffffff", "eip86Transition": "0x7fffffffffffff", "eip155Transition": "0x7fffffffffffffff" diff --git a/ethcore/res/ethereum/ropsten.json b/ethcore/res/ethereum/ropsten.json index 6aad92505d973f2e6c41cf68e41626caf68a60f2..bddf5c0084f71a7a83cfa519f17791802fa99f96 100644 --- a/ethcore/res/ethereum/ropsten.json +++ b/ethcore/res/ethereum/ropsten.json @@ -9,10 +9,6 @@ "durationLimit": "0x0d", "blockReward": "0x4563918244F40000", "homesteadTransition": 0, - "eip150Transition": 0, - "eip160Transition": 10, - "eip161abcTransition": 10, - "eip161dTransition": 10, "eip649Reward": "0x29A2241AF62C0000", "eip100bTransition": 1700000, "eip649Transition": 1700000 @@ -30,6 +26,10 @@ "forkCanonHash": "0x8033403e9fe5811a7b6d6b469905915de1c59207ce2172cbcf5d6ff14fa6a2eb", "maxCodeSize": 24576, "maxCodeSizeTransition": 10, + "eip150Transition": 0, + "eip160Transition": 10, + "eip161abcTransition": 10, + "eip161dTransition": 10, "eip155Transition": 10, "eip98Transition": "0x7fffffffffffff", "eip86Transition": "0x7fffffffffffff", @@ -52,6 +52,1528 @@ "extraData": "0x3535353535353535353535353535353535353535353535353535353535353535", "gasLimit": "0x1000000" }, + "hardcodedSync": { + "header": "f90214a0e11154bd22ac6a45e9569882d75fca57d12e44e5def1050de0a7b99452fb80d9a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794006b2b96ab71a8df73dfabc11ae790f9c8c259a1a0d6216dd88ad006659c6214fd81887150e1bd1f87b248316d3a58cbfe40f521eea026802d5f81a58db8519436e768eae8b11f7de071d7f274487e5b5dd9e6954d7fa03ab8cae24350c9290ed5bb29777ae22498e0b8aa87f0ca85f2bfa74fd95c61b3b901000000000000004002000001000010000004000000000000010000001060000000000000040000000000000000004000040202000c0000000000000421002681102000000001000000000000080010000000010400000410900000000000000210000000025040080000000100000220100010000010000000200000100048008000000000000400800420020004000000208010000000002000000000800000000000000000005000800000000100200000100000000000000000000000000220802180020000000100040000010200000200000000000000000904000000420000008000000022000000000000000000001800000000100000000000008000108412d5f8f8832f60018347b78483449c5b845ae0127896d5830109058650617269747986312e32342e31827769a04a1c4062b2593568cf5fe861cd1b9b9024189927a3c75d09471efa029f7483b08863e584beff5c7905", + "totalDifficulty": "8154014315272113", + "CHTs": [ + "0x614648fc0a459451850bdfe353a932b5ff824e1b568478394f78b3ed5427e37a", + "0x1eae561c582dbb7f4e041998e084e165d0332c915d3a6da367638a8d24f3fafc", + "0x6fdc50234f74fd6eb5b8bb28378583da28963d26c7226e1254e04b676951c4b4", + "0x0d40b71e8f08c4d93a0f62d93eaee4c6ba92ee1ff6e46dab57dc64c992873245", + "0x7f129d232456d600332b59fb30434e4c71aa95b0e3a4f9328c74ec5b97102ecc", + "0x0aff522a9d28bb1a32af4524d049ce9b3e66149e8d2644efbad5736c9fa044ba", + "0xf9adf08eb7bca9237babc37df559297c6e1b54b585867867a2aed7c10cd191b9", + "0x5b90ca8fa35cad63ad662e3696f41c4159fa871e5f77637e018595a626282150", + "0xf33aa648935eb7dd99906d0855c71893b4c0d0c9b32ab8c8b7ae316e503ba35f", + "0x79946d20c81f7aae18f427d991f21888169c96424813461d04a61758bbe9f214", + "0x80950805fc209691ddaea24e8afecd0096e84e3c465940508131f41f569d559e", + "0xd58de59cb3b7f0323ff3edaf9e3af56843258c457f490346cf25a73a35163ed1", + "0xcf52cf0f195caa769e2d5857231f31bf17efed369c358e7ade257f6278b1df0b", + "0xce570fce622e9f3bd03942c63537417f96250bb3605d2182e5a567e961739e46", + "0xe6add695b25d4b06bfa08a80e9e9295c84948fc34202cf24b42c1cc325075d04", + "0x4699a5f1df0d7b2dab8ba282b6e83af0d0b31b604443e13ccf097fd46f346bcb", + "0x1a16f27083787c5dc1dd8f0be868338c78ce147d0cc043b299870f568de20a3b", + "0x2dc5c4538c1812e892e0a53c112e8e4c3fd9f6f60d1e8531a9df5419df86d013", + "0x40de720fa5ec3b58b3be838f189ae59bc4f21bc8d8f406e569339ff054f93459", + "0xc7274c13bc7462d09cbe60044ea6c9e92622204c41dfddfe864d388f830ab35e", + "0xfb6b099472c5457a813698944d4901b766b4bb8a177c4d357897f1974ba6c431", + "0x9e6104a79f7bbf96d117607ec87be8604417d80af11f558ef1e74067168dff0d", + "0xb8deb38b0b58be7b3eb89a5a3ca1eedc5726ab14186baba17ac520a592f68827", + "0x59a58beef1250fb3315291f0b84480b947672deb331980cda261c94ba544e22f", + "0x0fa4b28513f4a3639b427b5abb87902273fdc4077cc0a51361c3dddc264871f5", + "0xf9e1e92a1de2346fbd8b54987960f08a8b21cb0a8a6c561edcccd6cdfacb46b0", + "0x89f4a9590d0865e126622cc47ce2a1ba50aad880161335e8c88c8a24af709fd9", + "0x5f014ca414e0663c92fcbda0c7250153445a04808c7d062859d000c970003834", + "0xbdfc2347806fb9641788978b5d0a6bccc81b11203cd751dfae45a8dfd3bde7ea", + "0x41420d311b7b527ec68770306d97ce3352d7fee50fdba3b05bb54e70c7250459", + "0x3ad7001f1b2b2635266141327ed45b017f334fc3a5f410e3e99624492c489def", + "0x2beb717939cc0404dc173e50dfbe1477ff32c1ccb59f40d7900c2337021e2d6e", + "0x558b53c5eda0866f7515f113ea3254cb85424b739a5dd6ad48bd4d9221006985", + "0x294c62ce078bf950361111adbca5ebc963d5f12c7014b04e3d86d3cc95c6faba", + "0xec18a7c338bbebfdfbd03f1896d726dbb1d21a29e32e92fd9949478078bb19d9", + "0x6e61bb0641c13be0e0858bc3da3a0b06f6a8ac462301a33fab7425e99d6ae14e", + "0x7a4378c70cb339cb4e3805d013f6ecd66ebcfed3dd1df972c8124bd7b888c8e3", + "0x5911bdce0868655abb581c9ce87a51b85b449101d6d52350c05ff1db30deb900", + "0x081f65d017c48967f02efec9781ac4bc124f42a31ad20104cf4ac6553fe788e4", + "0x89a055c33ecf13f71637fc1dd818c6c26e86f4751c2e09c5b8970195b39ddcfe", + "0x83d33e35f31824d849e27d2cd1d4d02812cb84c8508bbb391ba563264ba2a429", + "0x144d66cd98cbac72f3dd9af9b2656529a00fe7fda8f70e02d0ee025fdf2de9d1", + "0x35d44c6849d379e58398189d06b149eee34a267e94230ae5e24a0c58ba3b7d30", + "0xe4d67e58b9be3711015ac016983bcbfeaf2ec71ee85bc08fc2e53e6d173ff348", + "0x67f7bfc9457f1a1566d9acd3700c399a2fbeb2e1071b3f5c908eb56f2bbb9716", + "0x2546b96a4a88411aa7b3aaffd71a0e22484cf526a099d58f1dd330b3209e4aca", + "0x91f9c4aa8357a4835ae2fc913ff21eb4790b711d2a813e94b38fbf52ced0c4f6", + "0xb4623b136bd01bbf2dfe3ccdcda850cdbc6da79b69324a582d3e1c29703a70ac", + "0x68649bddb9b142d43905ef85df986ccc35c5b0796d860b367c21085c9f1d0220", + "0xa007ebec8e3242c209a178e01eb7aac3666fe09370dc1064bfbc46651c5c4e01", + "0x614125ba7478c7ba2ecd062eb8014ad3b50f4d9a7ba546c932e3f0a3c43c13c9", + "0xfa579127b6d13731bddb5c27cff6bea2d0ec13bb6d17679d4d9e1ffce8d7dc2e", + "0xdb11e78fced06b1bd768da0d345f19c023d5708e7b57bf09c37009887e0bcde8", + "0xda9a47b636cebceb2f378d9149a4a5bdeb6841746e7f677942c26629d3d1d4ba", + "0xeaf7cf3816297b48236ea2cf3f60ece988ca7cc97f5c8efaac1433d23c3a5ae2", + "0x1b7746aeeeb0651a867a3f1d34eb0662660aa667847c04d6761507f043d0eb71", + "0x4e6a1631fc6702fef39cc0e383f4fb5608e8a1f0c721b1622c8f25ea14aa86ce", + "0x9e96f3b348c2170685fbaa99c968fadb4bd9dd621f6e913784851ab1bcad60a2", + "0x65df62cc768c87ec256f7308d9f199040aafc24c03d5c6d527bdaef9da365e4e", + "0xdf067410f903d878471fc3b53d020bce9d0b672ebd51df16ad65f4a3468be124", + "0x358cecbaca6506567a60b08d00c888c7b1dd66ccc292ba7c4c9aa3e37b018391", + "0x5102782ac246337226e6527ce651294bb5c3787cc68219208d6dfe6ce398f0b5", + "0xe6a48cff6189dc26c3d7242eacaf1e34c81503327b18087840a755c322665ca7", + "0xbf599651ec19034ca45bec20673f41813b43387ebbc596a57384a813eb6a21b2", + "0xa5a5242eb4e6fecf1b497a62de8138401cae35cad4a9c136d9efdfc12fd53d60", + "0x05eddef47694a7dfc61e79f78b65bcf9c5e0e65513cfadd2957ac3a324f54b55", + "0xa4b5589bca8121c6bc00a846ec459faa99441d0ffa4b577a45984c32d63128ac", + "0xc8a2f9d7974a1aa77b35188d9dbb3fd56d5a2f6c796d2a665360374886c5a386", + "0xe9d430f782c0275eab831108f934ea3bd589c6c72f9da930e9fd392de833e486", + "0x71e85321e2d1a4b0eb48d8493a97ee3634fe153f30a85d1dd55fb0f5f8c82b91", + "0xfed03a8dffe6cb8043a11ccad791096151f0803b020e4dcf33adc4b180551fa1", + "0xaf1beb7795169537916bf4c9b1515effd365cc0e3988cd3e296fa075831fb63d", + "0x06ffb84050198793b580803a41e78d4a6fbcaaba0901a6f938a5ac5f9982f1e2", + "0xb416bb6b94d5076df719edfd2e2bb9237eb2171b9ebbcdb082e1bb1016244371", + "0x60d6170c0fe77eada1dd85af74079ea0387a167341df0ee8ae110d0047c00fef", + "0x15f03d5f617db0bbf963d2daa06816a768b21794d05873131deb677bc3ff2a07", + "0x0b9fa1e7fb11e3c0b33cc609a87dba8c1acca7476a15da2116518d72ea019e0e", + "0x9bba5ba8b88b76613a52504bdde96ccc1064f3e868881cfb4b155d24d237e093", + "0xa8a77eb3fa43914feb609274d1ed96d51ae29028fa59e7f0d174b085d106fd27", + "0x142d2e126b1faaa6997553835ce1933083a5c2cfc9818ae71d559c8571757fab", + "0xe9241f795bd887173fa93f9e72bd7f07ee58d69dede512fc31f9faf0ddea8e29", + "0x526d5379c00d1a318409ea42ceeb7fa9d33a4bdb71793c940e0d1b42cc848d36", + "0x2045cd5b32670fc4c36fd32e45437db69f468aab51e10e32da423ad28bdf85b1", + "0xf0d0bdc64abd76e66be96239efbd0f25ae30bcad096a4cf28b8b5bd9658c8c3c", + "0xfa5cf979a921b2c5ba8fddc330cf1df7a445936f7bc990553c578dc3ca45de49", + "0xdf2ccce3cf15b30d7bef7ca83b45875e96f02e41c6aee9a1a97025f0dc75585a", + "0xb61fe42d47707d473a2ba8483dbd4b9a40c1e7b79c2bff15ff52aec560d505a6", + "0x068ce2cc3d65a152aa574be6f0617ff7e2f688db97b020d8ba84e81ae9037f57", + "0xef3c631c846ef30f6dfdaa901d8d1424cd100710335805e14226976e719cc2b2", + "0xd582c2e6adb0d112786bdf27dbf1c77fc7bd002511e53d0a10f4dbe780156478", + "0x8a028e29f728e414cf7e3d403bce9de7766feae6090ae0f7f787944569f2b001", + "0xafa8f78b22031dfb0b06004d32b6699421586ae779624fdb7153854d1ea5bd4a", + "0x7ff5e562415a97c4f731c7f8a6b82e401705e34a78751a6e8039b0930eb0dc59", + "0x4cff7c84dfe821e917e48089d3a25a88a84983fa5836d7a78fe050915ea86374", + "0x2ba7416bb329d81424f9fb12b43a285eb3077799e71cd21f0095845d2d165c87", + "0x613ca539b0ec3a5819424d8df2f4ee08e03e5e95a2607503bfaa8574270d163d", + "0x3f6ce1315b8feca300da0bd990b582c647b4440d4a218fb4bb6c841a789da37e", + "0xba8b0c45d55e44cec801c724ad6b6b699fa78505d87a5f356f6ef054628f0e0f", + "0xed1069e471b099408ceb19fdca42cb57bca2389dec54b8fd283db0a53ebe2c30", + "0xcbb375230e9aa4f7aa8536f42f4df3a6bd7572f603c03e716a934b0d3bbe475e", + "0xa8954ae2e84ed2c0f45db6e41731a09ebffec9b064cbd12e020e66b7575d2c9a", + "0x9229f2300310a0675f281f76c695b06214960621aaa4592e3bba558c23b5bad3", + "0xe821e771c08bd42b6d926bc1ecef87dd846110693f59d71cc79a55d7bdec4cf4", + "0xa7e54537a3a01524894a0f602bf010043a52c8c7ac7f6cba10a6ebf86ca11820", + "0x3ef166570a593e608afe6b848ab8543957963e63448b7a1d0febb8127003b527", + "0x1308ea66fbc0fdf84680f227d6425e63dd850286de1783300b64ce77deb696ce", + "0x14676109d2f68c5cee4f58c27f40579cc04663dcd01017963402aa2f06fd315b", + "0xfb9434241629e83ec929e1f3feeb1943945e8ce87590eb69a9ae44e3fdbdcb4e", + "0xb695c39514548fe5240723355a6a2eb2d233a48018485435b2d9a63a1097ef55", + "0x9caf7a3660201b0118736033e479b74e488f64a07a79e33f4782048af424840d", + "0x82f188de672c6e1826d70aa5f7d54f499aac011139ecde9d4d05360cd8b9aed7", + "0xdf7367edfa34b79be80c06aab16a54e7d1f74eb9f813a080a8c4af916e3a61c5", + "0xbd7cf6ed130f745c853777d762706e9050c2a45d01839b124d517e3e73206c6f", + "0x474b7557c158d93d732fa0b8829b40c2d9e593206c3c36d7743b6eadb0067689", + "0x1ff6d56aa85903b2a9710b56ad4760825edc5343cab855bc966cad9bd11aef7b", + "0x52c4c3990413706a86714b1dcac8d9c4e55a36eddd176dc55066cc39e308175c", + "0x7a63b6427abc9780a14a4826c45a313fa2d020ea33ae91d48e4cea28ebda51a5", + "0xfa7ec58ce730e3d66c79b2616e14f11b87077c5d911db6bd89cf7c69f9fb2559", + "0x58f1b88f3ae0b76dbaae0ac58673ab38118f5253b07ee2a309cffa1c3f9f3a30", + "0xca98407f9cbb2911422f5a0716c325e1c2ef053355796e47eb619094790da45f", + "0xad072d0b4fcb8ab2662e3d27f6c6baa2f914bedcb91d178a9ab02b9646e6203a", + "0xd8319f885666fd19a5a9801005530e4a490fe29765f9d60c38b60c5b9ea92d5e", + "0xe95daec9ea36ec00b4f9f7478ae136c1453bbbc3530f839e93483fdef4a61cc9", + "0x6eeb1a044cf3cddaa87f70dbca704fe9cad675e82659380fb733153ad452761d", + "0x89c53801a896ebb9cb6c3a881269a93f610122fe9c3132ec19d64a6f22e9f7bf", + "0x81e57e25ce8817d0cf3ca17d099ec20e75f11d32464c2480d2f63a53a22dbe47", + "0x42ddde6c79ae71e64ab091b24e3db68c0c3b560669aeda58107d07f61d9bd39d", + "0x6936a2002771717810dacc23da43097c7c1bf5c2619975f55132b5f450c03047", + "0x0dbc448620312bdf04e118caad4ce1fc5058d0c14b1bf655b650677a2d1eaaa6", + "0x108dc62f87065752fb77f6a3f00e9141e1fd170b9c0538124739bc75d5632b4b", + "0x9b555f75bbc6b9fa5ab1b63b5e7a4f6efc7e9c659a8829a69ec591b968133478", + "0x3f31b6d6e18c7afa96a95ba0855b52a2b0e3158d338ce3a20f14c14078abbeff", + "0x78940ccb7637be74bf6f1735c615054e632ce53410b77e9b1d9f256699a54861", + "0x71b5505cf3b75c328113ffdc94e8ef89b673838a26bee4bd7ce582049592e54a", + "0x68ecd3b26c50447e6483aa0adfd4187a4f99bd1b8517772a9b90f939b322687e", + "0x6c291cfad552325bb806ce314f066f3a7af0ccb5950fede839023b8140b3d06a", + "0xa32f442c4134ff4559c3411aafc87649ba6776f54e0148469b7538c243c19787", + "0x06ab81f6da34fadcc2d1b52982cfc69e20d9451801da18672dbbabe4abfb6983", + "0x8ad902710d008f2b7bf8dc50b66a11045c7e10f80be12f6c5e622c05ad48f2dc", + "0xb85a2abe729f4f7bfebc6a7eddf085c99992d6af700f487a9a2a64164dd14abe", + "0x11039611b2f406125519fa4d3f1cc7f4a89a284be02f0d23971b2990883b4b1b", + "0x69e21960b1ed6da34778848a92b200c8275ec8ae1763e07b8edab481cbd5c1cb", + "0xab4a7ffbe54a07e8b1af0eccdd1fda88d72a25d61bf6b12c9f5022afae936fc1", + "0x8d8fef9f7b0df3e2cbe6cd99ae3ec231c3bf7a583028c0f6accc38d2ac294019", + "0xf8c75352c289c74a797e8673193c505600611433730e415ed877087cad99d3fd", + "0x28b756fe2c8e5c6cfb5df29e8c4f3a9ae149c9d7408efc31c57e5164e9333da1", + "0x3912bfa54cdcb8e6eb1900d7b8d464fab0aa97593b9795802d8a084d66d82288", + "0x0db191d64e9a75039a2fe0817308692f3a163bcd1cd6b476fb5e04cdc8af7808", + "0x7df00cf34abc055979b1dd70e38b364f0b1f9b6ca8a2cdaa8e72dc1c30bf5471", + "0xc4ebe3625665babdf6b8862da0ab84be8fd7b5c1e4849e9c0c2277df99c1ce4e", + "0xd6b86c60afd43553bced67a5e202b5dfdd62e668359eb25e29c4768328136fb8", + "0x46be2d0335c8157496ce74edc96bba09b3873acbe46c285c54e6985615519fb7", + "0x876940ef8cbf54dfda6ffe0d469ac0fc9adb375fdfeaafe3da8345a9db0c964d", + "0xde7c02533643f05dcefdefa9c7cce1f0583a655fa2a9b4a5040ba6a7489c650e", + "0xdfb9bea572e6a60edf6406a5e87ef3fe60f5e5884ff2fdbd9ab5ccc7f0bc602b", + "0x56b30ee04caa1d1e8367f58855c361b43c2c17361e5b242e3b6dc1e53778df5e", + "0x5da53224eafabe23f1a5637ae74cac54adffca2f18fb4a2f506a617370f06378", + "0xe1f7ba27cc89f68862c9cdefcebd4b63419d01e6cc49122f7e6187912f3298df", + "0x67d0b1d84379e0f67466a6ea295a94cd55ed67392e51a6012994218be921d805", + "0x9db5e16e54e0f9310e578d5dc943a5e45ef5b3f5e66bf758d99eb8a7528d2013", + "0x4e0b154a0b06d6544950634de6bbdf511735226649bd694b4194c43aa7e89bc6", + "0x8146ae0e77da83a036ed8b78046f204defbc771ae61bff4016cda3ccb6597cc2", + "0x00cd2da8855d464a368ccbd92907baef0fd0fe2aa8c24e48e4bb6854a011d746", + "0x261fe32ea2985be3ac260b68aea223c4b8e4db240e5fb182490407a062acea5d", + "0x65eee7ac2b1b90a0f5a4e0950b2a0f3a9b32e0928aad7f4243d4bea2f056641a", + "0x7d9e7db3a97919ff2204736eb3920587d8fdbdc97d91f14049f1d41a1eeb78d7", + "0x9c2edb4a5bb85fe484094b0e033cd7d31408a2e2d4b801a001f6f886b92e73ef", + "0x1e191b4674a3a80e09b68efe4c4dd4f38d344ac0b42a1fa152aa1242a5b53e1d", + "0xf498295a3d4e3b72ae1a11fc1d1f031cdcb68aed705be3fd698b5b92bcdc8cf3", + "0xcc89f9c163149bc17d5b0a1d79bf5538a86c4aaa3374e202a7b41cd4a562c6e1", + "0x11b5219b9b2daa6832f206bba106c64a2b9c3aba39f7dbad1feaefd788c9ee42", + "0xd936254f54f45c34c79c8a1ded63133c9437e82c99a26a8bf3efb95ad9b5cf54", + "0x8e8b502743a0ea7075c89a88bd149b8f97846dd915918ab8cb21c7b7b710a4da", + "0xdf0e67381564465619d90165d5677c19a396674d832530c18cef2a348f9671bf", + "0x6481f7abc12e1a5655b0c88a693ecbbe5ab2685c776eb5a9a3485167eeaf95c2", + "0xe9ebad32fa87a2a77c82a6d276a18b0efd6f615361f0d6e72c0a037b35bb396a", + "0x3e052b0aed9cee87f4d70626837c1a49ec169b2e8e03c37fc5616bb0573fe35a", + "0x15245833a84147b5df07f999c6baf27655bda682a5a4db93879f41a2ea023f31", + "0x4820d25f27aec110817a81bea0d7b9bedeeb8d9af55ace79de81380093a02176", + "0x4e156aedc00095dfff29092326b02fe2863a6aba3ebf8add79485096f20267ff", + "0x3b63cf9b1decf710ac331a5db829a5fd2eb0a7da6724c0a90b9525334092d6e9", + "0xb54145d2b41dd0142f5d2487b0db7fa96f42621c08d63edc036dee4b072eda08", + "0x8084c6690651740db4e67dbb0623b5d1342cec0ce95b6349058d7051fbf186c9", + "0x9c6b1c3b15784111ae80cdf7398e1283f4d68a9c2a6a2cd95b8594e0acee752d", + "0x71322541265c9b5d5404e9f089e505ef1814fcad641a81ba3e1bec009abcb920", + "0x1e028666b6fe44f8c8b2c9f3fa8c8ae5f062a7e117d79d661446fcb8a72b5db7", + "0x3a6cd5395b4be2aa1ee4f7d24d1d1433f46db1307c7ae823803ca1df7843ac6c", + "0x64b8b040c6c0cc2ebb2f011039dcb091d4c8f4d338b82fc1a68235c6eeb8c8a1", + "0x89f98b1f7e78b539f83d45c5ce8cb455e8ad7686f42d39f9c195d37ee9cf5fe4", + "0x14abc82071dae6f563f272da9e1ba7f9ac6f0547544cf9cdec088ac6283eb464", + "0x6aa6490aa64733dab57d954b9907f421c6c571ab2b2cebbd833d59d74ba19be3", + "0x62e4524b07b9b8d8a53cd29e457a58dd9f940141797f847b889023c22330db0d", + "0x534a4c4ffd28cc8c3f9823826644138c9207255958f3dac5d0a5f279d57508d5", + "0x3e7f74f25707ae6686334c585603253a21764cc1adc730353c02a56f6b7f3e81", + "0x59cc225b1c9cee2ece86b3093a8bf6ad3ae0260ee760384f1299b5af75eaa1ff", + "0x15ee9a6c566674f1f7742d1e07015fcd66e22d7648ba25be50428006efccd026", + "0xc0668ec3f88e6a9f1097050e999f0082e9a39ba43fccb194790e51a41602e704", + "0x03b0cdd4ca4ca3d8b1270eb9b73dd28f22ce5681c3e0dd5d11107719d2614de5", + "0xffad54056c851b02f83948e7d665980f623643ef40aebe91936c3bf0d24a3a2e", + "0xdd587650149a81f7a5e92739c18af1216bf32ff2cd7b1c908a6d3100c052b0b0", + "0x1fb346da4fa3c0b7d1acf87e4e8b9c0b05cf41d21794d9db58b14e321f9b6bbb", + "0xc15a230b3105e4c549291228b28a3ded073e7061f56bd31a8bbe4570e2e11910", + "0x9c01dbeda0dd9cfb4e70e8b63551c590edf5be26c879d319781154c44520aba9", + "0x8b418cd11db8823868c9495d213b9e5c5e0d8b25d5f7c262f21b3d2b9f591ea2", + "0xff7cbadcb60f8e9574e1a1ca57c0cf1b5b26725a7683a870b581734ce906df73", + "0x0efe88c68a8404cf9e1cee6d4b863c48ba502c5b5f504dd8a6f251122138e651", + "0x87994dbe88b7749a347e438c76273ba5a7b8835449d32ba75bbc877979e5a3b8", + "0x366779f7f70fc10da80427ba5f0f6af0e1b5d60c37c0c7e524e408a1e2f261eb", + "0x616c1a9eeb13a026372b099a101eece0cf0d1715dddf192147d401c722b6a6f8", + "0xb35494e9758f3bd7bb23fdd9c86f60a8bc73e3422cc9be80c7f592fbcef26b9e", + "0xbfcf52fcbd301f4b5d32e62f284f95d01bc545e16f806ab64038d11be74f8638", + "0x3f155939a40b556e30ccc4b32273a3586fd5aa89c4209337da1ecf145416c6f6", + "0xc23227be04320beca30ce9ee2b9f74bc8802414ee78754d7af1bc887e1e260f0", + "0xbc45e7b6bab510e72479ed0a675a05217244a9879be26d24780a39537c233f10", + "0xf651ff7ae3c7570a95245216c9461fbf90293a3962f8883d502a92277f0e4ecc", + "0x92b1b6ba3f6bb8a949225e51a480dd63152147e98d96ff5aca2ddc81eadac6c5", + "0x368b2b21a1411fd2d651c12cba2f2c3d4d9dee020e98a63aeea861e4b6837e12", + "0xe52565a7d159275cbe9e024130faeec3ddc3c3e27507d35d92565c8a56e13c64", + "0x3252703f9e4b7ba715810126ba57d87bf585fbec8e5928aa598c577f11daf792", + "0x6283fbe3a436d7860c0b564f2c7a32814c189093c775a8d800ae24160dd3e72e", + "0xe76378b3adf8f756728c553a5d822afa016d8117cb04a24b1c2a1eadd62421a3", + "0xc0a2504fcbb69d1adce70da836a73fe8e8fd74aa7aa4cb7d22bf844e7b91aa89", + "0x4d3e70bd5f122962b3cf64de57c69d85194126d71e79c964fc4397afe252da25", + "0x7efda1eb1564ba1d295eb6d403d9900b3336f081f2bb6de2c375889883f64097", + "0x6bdce19bc957c2bfc7e49f91d436367374847fdc6daae2e621f00aa024f78094", + "0xf42d152632eb0a55173ce7ce4cca3a91c7d7971a7456eb6ffe88dea54c3182df", + "0xa6a2353ab4a3bda46687cfd3ec8e349f2ed61e449875b9d1580d9a2370ccd54d", + "0x3aa4b80ff21dc79fac0b4f913d46687c4f991e5005fa6af731c9b2d10dad9c81", + "0x7d1022dc5a7fad9c0291448c56eb99283e1e64c37f6a55431843644102959d6b", + "0xa918366830e62330c423ce2f03f99206f6f3c1bf7660e01fafdddc40c8a0b650", + "0xace8ee8ad7fb40d481f20b165ac8012f3ff69f0bd8fd3ee290d894236549d065", + "0xb03858a13f584d7128989a47ae71d945dd67a3c2f54ecb3368ab76371d944c6d", + "0x56c78acc4c4dab9994018a969c3d3451300f8229d018f6af2de91fa4da6d4230", + "0xfb4313d83488e1003d38869fd8e8788d7e74c124542774ad7fa75971bdc61131", + "0x1decf505cb4473ffb91dc428710d2a2188d57bbcca71b7b5e277eaba0c7252cd", + "0x9db499b44cf0bd96713ec99cc769c8454fba3729014d87b67c18b55f0b4b9fc0", + "0x3cc717a489465b21a09c3fa19453d2a8cddb914b7d2789c21db6c311386e0f80", + "0xce4868287d923f8b22aca7a1df1ee11685d7c50c6384994ded2e99fcd2c01906", + "0x06b502cc70d1ae3c79e44a421b6ffb9a1a6f0bc647b4421cb375ab5d8474c713", + "0x4803b5e78af29ff140e37b23bd2f3de9fa6cda3a1bea2361e838061490708d0f", + "0x874051f592d34ea3684460f094a78d0e1be3bcf49586b1467be0386f1f9e3d94", + "0x8b66caf3a57d9998e2d88b0e4ff296825b6dbafbded01e384fd87bf4a92c5bbb", + "0x61f6244709692f492cf345586212374f4b209f1d2f92d3ba0e4000ad0634bda2", + "0xc58788c015dda34717586db2395a654f49ef0bfb4642459cc0ac5a5d74a2fb67", + "0x5161b49a84d5c614d72bb3e220878afa94d9acd30cdeef78873dcd91233de390", + "0x5e358365657197bab7481eab0dfbf8a6d66eec8dc62aa345c5364f75c07b8675", + "0xb358c9e7d218bdbb42f91752d03365945a474d49e84dbb15982e368def01cffc", + "0x908bf24327cfa0001d13fb5a0a73b536132e7ecf66d47f2c77215b03bb5b1693", + "0xb041e9e1a1087afbf41d87aa9e56195cc548527c81023d43416720c025b896ef", + "0x48ff5fcf252fa38a00aaf887e108928755932f45d7cef5a8fa0b72d711789f66", + "0x16f99b5d528b9ea7a92e5f7ac59e60d4305b71c9b20485c7911f84e3ddd1802a", + "0xeadc395d16b240b8412e14d5065a33847fcf8ae122d22f24438d7f79d8cfc40f", + "0x560faaf4e0471cfe39aafa10c9660d61c8489d2c5d49eff2a8e62ab31b7ec08d", + "0xf1c8bac46e0764d0faf89556b0080cc7b16433070e1e945e83f1ae36595da7fe", + "0xcb7409cbd3d3104a4b5d856887fe6d390643b4c433de855644f2184218631662", + "0x6eec345c4baf4e699f0ea48bdf54b2e74f98020395759e275e7cda0138424ab0", + "0x48a8bb061ad00561541b4e19009d3e8c2e2b9064337ef431fde45fb2988136c3", + "0x458b21c6ca362ec731487499486c4c0ea69905c8f36617810009edac608b9e20", + "0xb338584ad517b679ea52ea9abfc8d786ce1eb55cd3f221782de658f75b23ed47", + "0xdfe64bd8632a87d995972376311f78a0fd390fd2635e002e3dcd7838a3b28516", + "0x49d9ca4420847c21e2969911b7b5f321fbdac5037d0d8644d6f357444f91238f", + "0xd654571d72fc11d7a86ed2408cd1f78e8009dcd117323c8970614ed8731d66d4", + "0x2133c8e084efa57df6e1b4f1af5515a4662809d722ec002676eaa99987a2607d", + "0x5bc530b5fae32bc84127fbe7feb7eb07432313915e2d579a668f46fdbe46bd1d", + "0x8070890263bff747db0122240249361f5aa66cf121c3b6c4ac40deff6e42af5a", + "0xecd7aef2e4b4419539fa40570a61a967a0f296f524c25de9cdbb012b03d8d837", + "0x4376d56fa5e09c64156c6d1371151a33f7ac1e46635afb37823ed75287083bf1", + "0x50d32b39873466597d5fd29a2bc3596f54051d064559c46510f643f2c24e6319", + "0xc44c4284e5cb873846761cd012e0d221763c972242c0c4a161f3fe0e2da74026", + "0x141d2894e4cebe94df908cf463d5aa6a95e22624f5183d03cb60011c0677eaab", + "0x1bb68700d0f6e052ca64c983352731ccb0c3947ec73d67ecbbcb72c5ae3791e5", + "0x73095bc9acdbad7044390e72ae6c471c8636dc0116e2ec322198bc080af7fb11", + "0x66bbec6ab32528486c3ad89c36fe750d9c6e5bec862ea683988ba578302db503", + "0x979820d20c32a076297e3a940ef4b9f3ef02c9ed551996fa106b6db778852e52", + "0x4da1d4babd26763bb29976f0f2c439d3cdc06089951d0eeab7df2da59c128454", + "0x4c70dd6d755e7fe3da5f94dd2af53d037bda9d7b1eabb68bdb75111ecd190e04", + "0x87ff13d01ebf46a5a45f5b29e7026117e8b41d135829457d80918dc00a325f0b", + "0x78d573b16ad8a3c3b0a704a01fa79915907fbddeaca6c7e189c5c586855ea882", + "0x95b9c87ed9b7365b1ac15b02a388d47f75ae8036bf658850ebbd8b829ded9186", + "0x812fb8d5c2d962725245a182d0d267fbf97358d5c236cfece3fb398fe641f78b", + "0x31db1fe9db91df6684660f1eed0928bbdac477bd1bd376399e27a53ce760ffd8", + "0x4bb0cc2765ae336b21fd1f983f21464e907c6fa4831b047386f64e021550b2e7", + "0x7f6317d849a40784edf0d2c7b32f57569e561efcb4fb3c5f857b30319a93e2e6", + "0x5921a9e8ccdb11e574a012cafcb05a6dcf86ff8006595837fce805675202b8df", + "0x5357bc6834cde24334aa697f050c51c0456f7b03f938819baaf8c5be1dc10df5", + "0x8fb8b01b64eab496c9496179016adac0dae5ff6793970b3c307202fb17a93af1", + "0xd4ad4478bee0d5bb0852461107c3bef946f147f66a478789b39f7efb517b3bea", + "0x630bd6a0422ab626b28f205ec9bca83c499462556cd8116556bb40ad0cdf0e66", + "0xb2d07716e5e20312f314cde99480110794d3cf70ccc786828fa25ebf97ffdcad", + "0x87fc5c3371e3ed8f9201f61768fb2683bdadf8c3f6a3039c3504107b07b508d0", + "0x9e67bf7118e42faff0aaf8aae3118867c692b27c35efcbfd057d562e8d4390d9", + "0xd96833f813ac20074274531949bce0637e06636b6ee8b875b4811f09cbc8c116", + "0x4e9f29f98bee2fdb5c5692d94bad8f87b6b2904258549a937603da0ab5d36b65", + "0x77c3b0bee822bab79529cbe96d6847649d9528399a488ba9e94484f7059c46c4", + "0x948d3eca01db52a52679fbc33ca30aceb7e3ff6449463a2748fad0982b75313b", + "0xb3084fdf4a2ea5228c35adb20017039f4ef01e97d99aae08d4adf591b96330c0", + "0x5bfb4369df41aa75c957c1fffd6b498be3e7ae858cd53f67c1c688b9c78afc4e", + "0x6a2092f54cfb6c26b0d5c8f559e5e9995c79873db7631a8f6397ef5ef815352c", + "0xb286ce1787331698f1f860e1232f6766a658d27390b7623d460136a6be90a444", + "0xf84ba9ae60fe7bae5d3eb5be6c6b85aa564f8d0a7ffc479d4b0d1c8bd69a2adf", + "0x467550339dad58cba021e3fe967bd5d81be04e6670488adfcfeeb0dab07d6b2f", + "0x5150476eb3075df5b6ab3fb54ded34ce76e06660ee401951652e484e9a3fbd62", + "0x80845f54e9c21ee96f4aebacad6d199416a6b30fd741d6e6c9966f58c54f1364", + "0x8264045a020782a7dfc047efdfaa103bc186e88c920f2c25a016911fcf33fa2c", + "0x52399e942b721929b9875c274ec6b5034c26f7749f6d26eddd99972370dad3ca", + "0xf03d8bd2bdb8ea0b6ae2e7bb55703520cff714143501f291a3e7199be5d466a8", + "0x40eed1aa25f1a87c849f5e02f846c991e7473af9fa0985e3d61bdee01cf0a5b8", + "0x3d18c888cc8729f63ee415348121e10b5d990c8f091fccce7e2d8f5c0132a20a", + "0x2e72d969de06d28196506263e6dba8310b6854b5069fdf94317a6d3e90752560", + "0x2bf249dd628dec9d4cb267a9013e050f0710daa2915e468a623748dde1a036e1", + "0x91c578ed45a55d0108656d1533d209de851f77d9abdb58265bf867d7aeeac5e9", + "0x88dabe83eea11db59d67db99a159c623498cbc648c79d829a317719b836540bf", + "0x5fac0c3efa49004a1af631263097978f16f7e6710ee7a1557432aa727e44e09d", + "0xa8506487d3c54e70b3d6e83eb72af7858f583527ba6f6a6aee6a3eb33750e538", + "0x62641830df38adc76a043baad1fbbdd8e49356c11b6565ae0b04429743011b08", + "0xf053195e1bc2db0d0451ab69471520d0a03370a8f27a4f62a1885702a36d583d", + "0x7197d7a7897505dde22c7a518054d9df7f3368fcbd109cb596872d92f6a86fcf", + "0xa5092fedfd64b4985e70ffbcd7b4522eb45f9bcabe4c478b1f1452b063d6d551", + "0x027251c22748451514c27aed0f091e7589600bdfe816ffb0a5ba99d55265b65e", + "0x590271f52852c220ae38b56cca9960a1fb94d5343a0090bfeca5e8b5bf82b2c5", + "0xf8df310ab62fd52c452122deb0f2e79bfb85bbcd0bcba7e6aa5a99a541233431", + "0x5d313f0071f2d885da1b1b5d104b8d78ae49842ff9a636ad2613ad9e90b4ea52", + "0x2672988155e5c27a3af3ae26e74d164d9f52b4c71b74905ae90466113e991692", + "0x330c8cb081da0372f6dded08c0036199e11aa8ab51f6f9869995cecd3eb16f24", + "0xd65721d9678bf4667c6688af76d2dba106fc51e8396b0af78bcc80bf4149629a", + "0xf5019f4da39e5ca6102679e5a0388a3850eae7bcd91c632d26515caef12c472f", + "0xe9c747dfd6af4dc29b5836495be42673a20bb2475264a2f9bed00eb8250fea7f", + "0x2486ab886ced7bbdd774371aa0ed1c177d6d187bda235a4a8fa390b704eeee8e", + "0xa91824527f5a40050aafe359817594b43cfddbe3977113207a7d5868507caa19", + "0xd44591357b62e80b8f5606eebd4cac5052efddfc3e6a8c5689660d6617f93b33", + "0xfe5628e6d9dc75f413cb12ee1004e1514769ad7085b5434f762e93db9115b78e", + "0xd0393986e30a8961e01ede613787fd2e6fab42db329d0e57bfd16b42cacc4045", + "0xbe40cc6ffd2d51f7658f48359d4b726c64a354a9d1becf189ed83775f8fb4086", + "0x1d06de0ea61fd233ed701c9520f18012143277ca9b379d39db62fcd0a4e7681c", + "0x463ffe1d54544945f4abd7e35cb7c90a4a809c25e1919215207b7d2a8d3b4fe9", + "0xdf128c4d6c4048d4fb0f7db6db091ce4de10d509c45736f5521ebc4ac93a7ddc", + "0x5ed827eae4b7e95cac552d60b60fa209157f1a8c6e3611c1a5f6f7a49e8a6a92", + "0xd7fd28964beb50a410de115f290a0140fbc78d0d4c47ba02afe9f47d80df5e53", + "0x1996f9b8f7efa3d860233141774f46c00b63f207219c2eac5e61318a9464d7a7", + "0x4cfccfc8a9623fc9b5ab38c991999f8a52c6537d984ecbb0c490ff37559cc51b", + "0x4aeec05c7d47781008273281bd01b36c5ce21916cf7fc9dbf79aa2c477923c1f", + "0x96cb38c2449f97c92c81f5f4317b15267c9c2f4ab62240885280ced5f54bbc53", + "0xab5e6b9e4c4a27aa725183699ae30a6f8bc8148bfe85d1e4d0bb8565502e6e73", + "0x23fcdee9cd75706e640aa55c4e8acccd7ba9591e724bf351a7070104cdc698bf", + "0x5f3433fb2aff1e3843785b2f2cba22811cac84061803a2c5fd045b1983c8ba81", + "0x5613366fa3c323b091a44d10ffdfb1f485160fb45094cbd52707484f820cdae5", + "0xfc43bffcd36963706e2bac564dea4d593c35c5156de02eb747bc557988d628cb", + "0x4f23fab93388bc79a879f85c3efe0f15606828e6034cdc6b66810a90cdacb793", + "0x62ebc41d0592d1569001358ff3f1641c0a6ad89e66b0fd1e784f060c9101dee3", + "0x12bc43cf16c8965f106f3749bb6b5d9ff3936a205d2ef5719ee637308f81ea24", + "0xe991829e5d9c26cd365f6907de1fe2f2153cd88a7d7e8e56224c9dacce442fa8", + "0x55679ce947be929966abf9953031fd172480b7da8be0b34344512406d93764ac", + "0xae1f80cf386c6cbdd1ee0a1241e738c245eb12665f2fd1a614391ba54c741847", + "0xad12a941df30e25584f19f5aa65069d919a2e0dcbcce73de8724f975abdceef9", + "0xb3478980a68ed409b58d35759d4648ce21ed97758a1eaa0d0f1ce6b059b6da41", + "0xbec5fc3e9184d2e46bf0ae39c408a7cc243a41b69a923fb2d1224638153b3ce0", + "0xec95c1df965aef7e6061a770e0c82b6dab0027dccca5ea3791ed207b868098ed", + "0x2bd64bac83283cbb0bb5f7c1d461261a089a40904574e2daaee97110425092cd", + "0xeed53a58f04a07af388b098bb8c1e49abab852736c84d69bcbb78b945f23b86e", + "0x2c2c81b8a0c362105ccd0251119ddb406b65562611b2c3b3fb9825dba0cf6ac7", + "0x91c93ff5548a0462f1cc7cdf0a5c57dfc1f21932dee38e25759c9e5719036fbe", + "0xbb1c1bea058893b30e38cf0ea2f53b943fd6bcafb86b0eb647ccd152aa42dad8", + "0x079da567e28f61687250a49aa9fca4c465245cb0ce02ff075b500b203c459605", + "0x691de81b04aab67b3276168be7dc43e220114005a4d66a1ce9ef2665cd2bc867", + "0x3deccfb1fdf3e57aa2d63d0692cb37f375aa457da52d88cc9cf277c32021bf45", + "0x6cd1a84bb6976f2599898aa46fc67627663738cdeeb815ef1f45762cbb14dae5", + "0x29c96ea5d11a5a761ff7c64b24a0892daeed506b8f7ab33e93d6eb6023b9b37c", + "0x0bd8890f06cc6187a1d1a029cb319df75018b067bf796591b0b0a07a2c33c008", + "0x6a3d0b949241a493d2eab974adb173daee1e3c791c2dce6bfe59b127f8557ba8", + "0xd102ccc0476081c8d399083930dfa59486a51f83d33096ce77897b9b0fe41b1b", + "0xf9ddd66ee324bc7f06544cd53f792dba6697c1ecfd4c40a44733944f7e7ebc91", + "0xc14b245a2f2d02ecb944acb218d76e311659cfabcf72f9eb49db59541550b6d9", + "0x190781209df0cb64a4ed6ceb15428699e6b67f3cff28b7a987988a8e29829171", + "0x46605aba1cbee0bfdf90b57e320823ad4607d9ea1d6698005820021128c472b0", + "0xfac04645717514b9ec56f85cf0e6cae5aab46e25e69e3cd010513d9598d56609", + "0xfd0c9f959513a72149bfa23b32c5006ea766ba2e41edd47f450090568d44cef6", + "0xf0560a14936e66cced54d4002599808b127f9e56260f04f144fe9da412577c61", + "0xc9448c9e8a3b09fd51ee93b77feecb8a8ba03e1781a3f5bccc93b4d1815232fd", + "0x3c41d49f2aa53e90f5fed6f372c620fff5a80f442894c71bd7b6c2f55ed76f47", + "0x0d9517fddbd945294c72ed49bf6bcbd88d84c9b3b2e257c05f64bb69c4d7815f", + "0x1a67fe64cb906429f47250e1a30858366e0204c3f39b7d4b3f121a46a51566a4", + "0x9833e385110465d72ab133f02348cb33433f38454f6e985b022720a9836e5015", + "0x89f653efbe7bdc469e2896aedb2a22ce5982b919a7c6dd14046db4eee3950b6d", + "0xe92427a7878630f65cd4f38dcee4c5d87ea0dc3d0ba1720c0d9f53cd84411b7f", + "0x1f692c127e4d0e37f55f24f5c20516c804dad968b5dfcfa000595b4682aabab6", + "0xa3557041302da75734accb2e95db335abb208626e1b9d20a54ec0e0d23a5e003", + "0x96bfe30b54d1f39b5693272ec7cea523e0531cb7fb805e69a820888ea747b3ba", + "0xca8f5d8fdd17be423ad5bf84d7f591466137a54388b65f5d853aa83ef25e9b42", + "0xafd729d01c437b299b29da02e34eef0b8a762a24447b96c0499231a17a84fe7b", + "0xf95f528a8abcdc0eeb66250fc3d1df11d0831a08b30d11f0f6f0f96b02f7ef82", + "0x483de90f340fd0035676305359fb8c83b709374676796c86c4d4b615ac779b5e", + "0x433f6ce08d28351388ef6310847d7949f4e92ec99d585c3cbb5fd7152baa507f", + "0x77dc56a6b802ffae812c3e4d8768fc3d80bf96b95a55b1b9736a9073ed0093ab", + "0x5c3c2bfb8cda1218a08084c4645e2a84fde3aad03ce00c3310b3ae5d80e25be2", + "0x3321f6475c8ed111a1373bdeb68609f52c4740f22e4f13952e8a6d98fbfae542", + "0x7a0c172b717acba8a7b874e6c88257cfe68d41855506ee909d84d23b028a7ffb", + "0x7ae3757e9cb71b9063d79db76109af96ff1a5e6aa1183b1b70150757243a94e1", + "0x4ce794f4c0c9273c14e6faa680e8837247c6a0ab503a82432f69529d4f5530b0", + "0x8a0ff3d87111ac19f2d95d813e384a96dae3cb15caf12e50b99816393db3a374", + "0x35adc4577b9532f5e821d6a39029cb898886ec9a8b938bde92ccb1df67d2dc2c", + "0x84976050b7ccc3c953976162a6406d125edae798bc1bf850b8867b3242076a30", + "0xa64d9c9681b1192185c9570a6512d8b21483534bf0ca7f457acc96f91070358d", + "0xb27f0ed2428dde6c5506770651e3cab0fc62b71be0f245a8f6a84c326ebdf327", + "0x4f71b19b200ccaf3d359eee3ad5d7c155616b86495a43e2a7c18d0740304aa2a", + "0xf22988f8c5443f4400af9895f655a84b31e871a68e74a396ea5334297c0fa190", + "0x37dbed2e2f7408ce439ab0ab09af040972021ecd606f517af45c195a80e3f3f6", + "0x5165c41b008baa5fdd010a9a17f0c2fae2a0f16b4d230c21bdcfc737e9279c23", + "0x96ebfdbfbc02fbf12acdf622ad7ffb8dc10299ee66c4ca956eacebc6073fad75", + "0x47ff8c523b69bd123ebc4ee8a81240f2bc1c4e8dafcf9b9cb5db5b7a8f4c884c", + "0xcc3a09c1108d216199d7b3412f86d73d4fa7585c393dd3ed95ef0320f0f9bba6", + "0xa79133fa77c790f4f12a9bebaf21531c831c72db93e4a36a8f2ea672260d74a6", + "0xfaaa6544245fbf22f3016befbd4acf93135e64f69a7aeab07599af3a52f6323a", + "0xf489992872d1dddbba3c6b2fee1eed0f604f02e7350d747398944bcf4fac3171", + "0xb49c4387382e339ed00cba9469275920e116ea556bbefeecfc9c154d2f1c1f77", + "0xfff493a58d86d6160d9bdaf60504b07d6f33412fee522af09c1a7de8ec9fcb05", + "0x584d1e0a2569cff0fc1b71c850eee0c1f680037cbb678bbaff035c49ea5a6275", + "0x91682c3606944042c10a5fed5e6b5662c39cf980471b8bc603042cf0d455842c", + "0xb979f301dc2bed6c7e60bafb112b92b22b5f173f1d9e8fd665415bace1a580e0", + "0x71d439075aede2a781f1a872dcb8c89846056f5317be5df5dbf47904d106938b", + "0x9c2353de49861c4d10309f99457e6f6d9e5b7a57360837aaf0a1942c0440fe9a", + "0xc2c08c3e2d8741a4b4bb180fee1b1d0cb54f87f2739aa3881d534a9c73fd2d1d", + "0xcd75923a659ed6e79c9a1e22201374bada6bb2e030e3664763dda54552bddb15", + "0x60749dd09da7517a03cf6baa9b4623198f4209f1274c00bfb234c61d467da12d", + "0x11b1893d89913c9c57f32ab875ed403fd9802475e3c861472dab770d4882192c", + "0x26ddfbfde82876e17d6872f31aca7f233f790b3ec0618d6b5c51871c9700039f", + "0x8c9ceba1e4cc81ee6103acda0bceebf5b0413023966dd5c3c5641453c96c29b4", + "0xf30e9e6f8456fb427f96f356eac399fbee64dee3ec4a2806701c3484608c0edb", + "0x7773f80e3cadcae69cabda956228cac8cf774bfa84e2ce92ab2872ed19946858", + "0x4d510670cd4036f534fda9c7b4ddc14aba28ac985b90a0bc831483621d44996b", + "0x4e7d6a0fef6ac54f4f0336961561398600d0c6a04862f613ed6eb648d45bd348", + "0x55c0bb04ee6e1aa15ae55e1de229ab5a4b553864c43cf527f2ee42d441bb0bd6", + "0x92a9c4000171a6534d7546749d7392a9a18974be969bd773b4ff3f5c39c91a17", + "0x892daebee1b6c833ad425d5606ac7562c135688ba8e83178c0f8438679e84e7f", + "0xfb1438e6e00a88a26dc46bdb1ff1696cfac94bebd02f16891c08b52de7accd84", + "0x46404590a94fdd59cdd4ce607c1ba016a78be8bfd8dac361b8cccf6191aa0bdb", + "0xc1a63495f12681da588396c2d30096cdc8c52fafbc0bb0d6f11de5ee2c5a7c51", + "0x86ce595e0805026d106024d9d13e0fca7b208155901b67cb0011138b31bd947d", + "0x7bd81277da5da97359e67e2d5c12cb238daa009f4220166e13ba1f067125888e", + "0x2dbf4b23c2178c8dea6066d43b5f1285894d9e74a8b1056d0ee02091b84dee50", + "0xe983e1211c5b12ccb547dffe850e4adff01eadbb6717adb150248900332482ae", + "0x2f42bd119e06fde6cf7e95c59caeb189afcec85ee9968196f7a15917f98962c9", + "0x1e54e7e17e28b7c2de1c8391684a938e1adc2011fdf8f4a6da900f3664f2f25a", + "0x657e31fe41599356929abc24f7d751e49aa769ef9a4b9d5b37e6bf019dd567a7", + "0x33de4f2776dcd8b17b846816e02c4dd1694891b6827f87a80b31ed37ac0c6048", + "0x2b3b692269fd869b7797c78de9a521705aa223d78652dfbb32c1b8b612bc4351", + "0xa57d205b64565acb103e962a83d300a302efdcd96a13f4c79e80e2733341a70c", + "0xbcdcd2d22c7e21d24e48d28c43d09186a15568feaa24f44f126e5bb96ffbde99", + "0x3a6de68513eb363878604d054f46224e8567f2654cbcc0d9f81fd37fda804a65", + "0xb762285a5d8172a6df584ed15e954f432ddd8f41ca342db7d1d14c54af21e420", + "0x97702988d087056b85f9046bb8d3e37365f4e5811507c1e2daf544af76f1825b", + "0x8d1dbce8efa6eeb4269e22b45c2a9e7c53f7ecafb83e8219b14d43f66b6fa68b", + "0x54a2b87cc430e350eba1271a9161fd08157e196a4d4a7ea29a7461dfab6370a1", + "0x253d68032f383891fbf9c1898fe7d41c6362ebf759b3e3216bc14bad63250181", + "0x175102f1ccd3712675505c3d74609f2c46053c2501998a91b69b2eac9e1e476b", + "0xb07dfe916a1c61b07571327de80a478aeccd3f6f26aa4ecd7081a5f16556e946", + "0x0c87d079935642a1d3673d9b41ed2d3da33ef124acc081e236b23c50d78ac968", + "0x0f3718936f3be6ac78445fe56dc92e4806032d67e014cbe0a92e3193999c8acf", + "0x88bc8add9c916ec21c2f83e398bee626533eb038900c787eed34cdc1ef273a2c", + "0xccd4eebce003d0415c0ecf2d40916651b5f336e5ac64652b69a2ef653d777995", + "0xb9bbc12a4b92aad64875c00d7536c7dee7d62b87813b99a9a77bdc066c169a89", + "0x9ec43027a7229ab550391eb6dfd0cd5cc9b3b103eeb0b79dc9d2f4d8ce3c0aa3", + "0xb4e8c06e7424fdeee2f241365828d9a407f117a0811cced5ac35dbe9abe70a81", + "0xa5a9149f1002457f7c9c3dc2a4aebf7d5453f20c1d7e3a3a732ae92b04b8c00a", + "0x28d20d654b1e7a024956a6c8f53bd1c097d1df3a6de91036092c88c8070474f4", + "0xa11eebe47ce50f4076209307fd58c75b0ff725469ea0875c658119f7abc644de", + "0xfff2020661ecfbb50cd6263686f903127b9d1e5f43dd8bd2d5ecb09eccf6090c", + "0x253576932466ee619cbd3f012915380f1e7a79972864fa0e98a755f902f1199c", + "0x554258855ef348fa770e3af434da722350e998e930f234b5b8c33d3d2f806595", + "0xad11b76ad542ca1e825b251a6cf410b6f86c8c4b42b3c8690a2871e7cfb27882", + "0x3c3d7cddca90448af6119481e08b388df75389ac3c7dcc26ac532680b23ce2a3", + "0x94c4ede45ad5b27c5a6b051fca3b055d548e4c29d2541a9cb4c37bcfff4f19a2", + "0xabf8f7c1b9828d61e3aa72ded1ceeafd6ad539b79b5d56c5eb93c3327b9d83ee", + "0x6e2aa49e5942f19f19181ae911e7951534aec1a7fe295bbf921d8f28a4b7c0aa", + "0x0f49df5c80ca4ff33623bcab40e9ded6159d3f167f7754c3dfaa2eea0ccc368f", + "0x070b14922cfbdf78ead68019e520a08b987ad203db005f4085cd25e6ab587016", + "0x8501cdc568d6c64cef9b87e639d5a0e3f5a4b7d77e8510df07bf0fc50d89d020", + "0xacb70d4cd2010d149f382dbc0f7fc1b74f0fa6bfb5e770017fad960aeb7c80e8", + "0xf49878d3473b21f4ff2c331fe48074ba47ff78198f839dfc7d1d69abab81e499", + "0x9a11ca4ae2a7a68bfdbf54d7f0978150176aa4012ccb8d1781c10b58e01b851c", + "0x58729c03b10b2ec8b4abd91b876ab57a2c0bf05ff02218bae9765a90037562b3", + "0x3b5b033d76798ad041842b9ecbe223dbcfc2d5babdbe864480126dd932d29fa5", + "0xe08ce9149dae8cbc8968dbabbd97624f917148f26f45b858e49c2c389bb22f20", + "0x6b532db41ccc7cdd970d5cb0166dee2272346f23e0acfd8d751491d0c3ecaec0", + "0x99fbdf39928b3f855b2390bb845a96478221330b7c350e83e7b4dbf7a35e2685", + "0xd2a2a108c0a4dfe5ba0d0488da0de7f6d15994f6c14b511712dff4af33bbcf8d", + "0x75ddbc2ad15fc25c2ae71750f1d5bde5e825b40d29d631a27d8801590d5e1321", + "0x226995d806f5d03722266554109395921f63ba1f66f5ed1ece7341bd860a635f", + "0xf78d4178efcc128e86b2e1df767e59c76a4bc9db0ea294bec79b08448133568c", + "0x160e1fa8fde835d6e91187a3da5caee245d4d0b9ee30894170617b680d275166", + "0x361d71c972953283f8ddbab4a45a01b39f30290dc1dbf1b6b744af5afb2e70d0", + "0xb8394cc0d42a0b60ec40984f753b8eb5b9175022bc2ca7f80d64d26106ce86b5", + "0xa0f6ff5ee727a3106e045468b5ed9ee8ef387eb9a0355f2249a71441819b7bd8", + "0xf441bc00d94f06485368e043f933ec9f8e181b11ab0212d728d83e2d039edfb6", + "0x1da12d2213cd65f5fce71c06e44b40a487d5e676558e6f617372244f8e98d549", + "0x7d988011d5c7da97b37c6a6936aa499a70aee8b38f8ed60959f576c108c54d1a", + "0x3d5ff693e4ec163fecbfffc95024cdddc4d2c561297124bb69c8d6aad4ae85b9", + "0xf7db67de101d00409e1ce3430445fe3d38a073e1c81e1453d63f4228a51615ce", + "0x335a94506606e70ca259348ae7157066b55faf09a4ac11bcc34a0b865f4a8057", + "0x137d0de6cfa1e475f6d94248c1e30bc10c17b98d40829974ed4ccfd7de0ffec4", + "0x1d63a7c1c54bc740875f3b5d8750aa6bc85d1c10e3caa4c9d67b4b010a83c29a", + "0x04e0208220bd1218b6180dcdfada95b3a8391bb380b0b0ad5a1a8a02f1a3687b", + "0x02a3dbb2d221bb8e8be58e12982445246928b9965caee73383e54edadc017315", + "0x549dfa1efb8c32071dac220aa4c76ccc038cde6ec9a39e2745a2d55e92bc0890", + "0x1f9bec0a6b3415194528a23443c9904f578d57d7024ceb964ab09a9e8e3d9c03", + "0xbfb25a68c40fcc0dd996e992b18dc73a48525a323995a0eee03a0a04c572b68f", + "0x7244057fe32846105479bf704b871bd1a081acefc57b37aea76116442805a70d", + "0xe6a48159dc041cc9a0d8eb124aebcffad394a400724410e956f8f46e1d89e8b7", + "0x812d9147b6661f54af0c39c1f4de79588ad9cb62b25befd4334ef5f3ebbc2cff", + "0xf9aa945fe2eb3d1e2a01db68d4502a4f33afcfa967fc06944e035c582dbf383f", + "0x63a437b920c6b916eed44003b8d35872acbd0d1859b6596b4f45e39d04057e0c", + "0xffbaa46c582530498f30e51d18deb6973824b850d09bf4a4535699890a548eca", + "0x6ab7423011299d3a82d2037cfac752bc0e88a82696f6aead69ae940bf202792a", + "0x2fc6f5dbcccf16340f2b3a5e341e364c3cb1dbb3e899c7acd6e50e7ab333f45a", + "0xd354b48b2d3cf13487ac8138caca0740408b42613b50f056ea595741ba275d7c", + "0x8e1cbf4bb7a83b1e47f69c08f3fbb133bd36d8fbce923db79a248c21e9657f8e", + "0x414c7f4d86ecf78c3988723ab584ff735633a37267b1f5986d0c2e6375f878b8", + "0xe0b0dc519fccf20ef45b2b185d791575d88f93011bcf5205f4880f7ed755a54d", + "0x528a7ac7c6a17775d207a983b76ff54f8fbb84b8ceb3f6fbb60a80d9f306c497", + "0x458bfe441e32d15fd95a33de1665a94ff4f2a4c357f4fdef5c9e7c82013f59be", + "0xd478a55408477b3559d50bcc4b5c4cc2713e0666f40d87c5faa7328c5b7db489", + "0x9977a36db4a48e7e1f4029efc494a341b8eb2e40c73fa9ed3be2f16065df8900", + "0xe2cb22eaad423da374bbe1cfb8f4f353eab0f9874084aac4f42578525e26586c", + "0x0225069114efef02e188a1a35320d6522c115706c7b87b3b54805aeeb9e60db7", + "0xf82e17b405c2e19957b63706c0d7405e09be93b129ecf850e1941d62f1c309eb", + "0xe26f6f439b0d33f00a973b12f5d3c619bc4cae8662ba25173fa3398b8fa19d02", + "0xeec38666a51fc161736b7b48c8befc98cf9683975abb8432cf735edb907b05d7", + "0x8d8c5ec1f83108372f361b0679a65bc72f1b5055a3b9a12f01855a61437c9ebf", + "0xb53f85551f811c60b17b48df2125648aacef9a74af403cbd877c1e539c045ede", + "0x498d608f0ebf85809b9f5a9270e481d03f4e26cfa38600e50347b968cc05b13c", + "0x8e6d9c2693bb876101c2c0ced0976fe71db31b0a725fcccc88cf9a69b47cc9bd", + "0x5ac71d4859c028d90da98406f2c739ce6280cc227529ef8f1be3d3547ef84da0", + "0x5e72e33c2d66fb9b31bfd4d423aa1ae7d4aa88a30758e29610eaaee778ad75ef", + "0x97345508a573bf5036243f384e1584dea6a723ae342a876b572526cf992f956e", + "0xccb2773ad04c605e9b4a6dede3cc41d69f43439f19e4cd97f54de3a28052d0e6", + "0x0d48cd6317b72fcdd315a6f912646831ed52257ebeb03fa70576a918282e43ae", + "0x688754d57dc88205d6223c88e802461852c5f930252bf74e08b73a79e4f0ebd9", + "0x2860c5f26b4eb0896b4ce041582a8d38b1c8aa21b6c163784277df2658d7e191", + "0x35257fb9aa6c9e8ac16068786fda9c06ece1cb2219106da3216ef4648d28be29", + "0x1b81179c25932e14098aad725e59bcb8da8867dc4a5d145e90103beff868f2ca", + "0x60309b91add03a291fce9fc3f8804eeb993508d7cd5ec4c13a41d31b71d09997", + "0x04d2e5e78c8d8c0059d829c16ea7397834cff5914a5c0618e5904a9037338deb", + "0xd6d345d35827336d086eeb801e331fe77491d47f0df668234e05c3755e413e13", + "0x7f29ddcbfc971fa8ec8e73ed1e77301310e99f677c17320a62fa32ca225e3e97", + "0x036d5fdecca68a54be820fd086e1185312bc9d434b332db547d65d9fd0adc5d8", + "0x07d7ebf143ec977f4134d782bbb3da778b1add224fc3617987d9b21c7bf7d09c", + "0xf27fcee747ad3f1acfee97988403537b85f51ac1157aba723ace11945bcb5115", + "0x5cb6c6cf872823930abb5798d4f1db9d0ae200d21ca3716445fcfe675432cb68", + "0x8226cce1ff6639ac646ec09fd71a0de3d6ffd323df6ea24446dbae7eba66a69c", + "0x6e07367cd7df8d5e756e9e4c535f0933df76941640a7bee501f8af8b653543b0", + "0xa60f86078624b3c727784d4a95bd6dee72210c801e7cd0a4fb01a2ef5f7e24e5", + "0xe3ff30fa3e754fec8605305377cf6a3842d2cb323950db58e5bf02794135bb2a", + "0x74203c76eba1b90e3e76fd493f66fe46130e13359547ce4c94919e9c8909ddec", + "0x4ad657902a564df2f15d7fcad6e4171c96c65798d8b49d3976c7d77869c06ab9", + "0x043cf965b0b5b2dae096d8c73190ad9b834b665097e6b77212359c5029fe8f1a", + "0xb370bc64167f8c6be57238f35a56b840688138e860b08c2c8694401ca32d22ad", + "0x1c242203268f4d543b7087b0b4506f4b5e5e6fa4f4b495c6bd15f94c7ccad31b", + "0x1250c4ef1fd54888a4e377127c9f27889fc5bae3455010733e8f48d7aef65c99", + "0x83bd5c6109c0937f109307dfae9b347fe7da69fceac9444da2d6394d26911956", + "0xbc25fde0522d9ded3c69f9ce8233145b5b15242a8f5d6d6feb2b7aaf0aec5cc8", + "0x0b7fd860e34f21a54293ba2dbd61b28fe5ea3399a29ff7af4f577e681ed579c5", + "0xb2d96f1f4fd0f26e226386aa94e306d8a4a2a3be2860951e0c2e39c77dc31bb8", + "0x4b9794e3a3fa08b8a71f55afd71f2c5ba34fc9d773c32242246c721c87e4b14d", + "0x01d6ab151bd597e2681e9c67a0c3f445c85bfca4f8cd73e3d088f49dd669ed2c", + "0x686280902ed294c99c49b8c2366686af45b9c6ad0e7bfdb1c52f1c901a6be2b6", + "0xe0a05a29c78f04794b3957ccb343be88cf11b00f5d2454a6003e2b363f88d5aa", + "0x798aead8eccab3a4d4dc1283cda04057038bbf80944a87c9e796fa7565e34ad8", + "0x866ba6664beb3b953f5b5524190685eacdd1830248bcb5b0cdcf686101be0ab0", + "0xf31b7929f0cd73d652d1ded75d5ac19d95720b386fdc660e0829d122e66a576f", + "0x20fb041619114e9beb6d70ed0a488a1ed8aa149ab506e7bf62244934441fe0a2", + "0x91f9dfc54ea16eea1700cf1d729e77408d037f43238545136f6cd9c72bb2d260", + "0x7f633029a0d96a3e4232ca3b02189b59b10e6edeafa9756f8db1e8844e9cba44", + "0x0b4f8a2e3547fb13fb7ee5555281ee325d068a76a67ddb1dcef60a6ae4b41597", + "0x35b3e9e377449e7b1a023573123f5d132b50a829091183d279590edc73df67a3", + "0x8ac0d74f10c0f6a60d2e5729d150ffef0467934f8c3e0b37eb7112eaff246a60", + "0xa8f65245b7fea166560a0d775db32688a0249ae3df937a42847f4f0f358af085", + "0x6d5812337f22e0e436ee2dee8bd3a45b237df7e045a4bee1458c55a1a269cfbb", + "0x6f79965282b544af683f2cd74cb056c092d5aeadea1d16e34382cd97113f4436", + "0xf91f555495a762ed98f946487f9aac89b4485c71fde9d700cf562164773b119f", + "0x92397cb86fcd4bdfd448fce9e3fd6fcda60586482217ab74884182d91f3a33e2", + "0x3f694bc4515ce3389bb57feb21ffeb3078e67c3a13bb53a345f7d837d92e91d4", + "0x81694d138eb5afbb7137ed7de9e4fd7010528da30014d06feded8cb2db307994", + "0x56d59ea9f640664f6a1ea3d92860f15ac38ec313d520b24b0ff4e365dff53b4b", + "0x5056000d0e11cf10bf36c57b44eff8fe9560e261264ba2b29e97323c3fdda5b6", + "0x427230af29e403291fed2784ea8852a68d4bb0c07a514dba4797644c8150ddad", + "0x21d5f70bde1ffe09c38545103cb9e6a349e62c1316f1e7e78c2f93e577a6dad1", + "0x385df64391152fe6e1f574efc1be371bf46606ba571420c601877dacfc3d1458", + "0x4076eeac8e1b22684b6fd996a11db49ef6b52539e481c1dbee3f1ad3f59d4872", + "0xa8239c7d2d32a3ef6f887799bef8b470dc52d0c82af8de31bf3a8e4a6bdf3535", + "0x52a6ab7c89019f8fed6761845f9071bed1748aa461b87b0a587b7f59489b2dbd", + "0xbf80c8df3c0202192d1b888f80668c5ebb9d4f8c7d168570afc54276af4de578", + "0xddb5784df235570dcbb1a08fd293d58f63b7afac494b1f8f947677f2dd36a87d", + "0xa741791df39f377bec8cede1f1c491eae35a710de095b5ca5c56d77f4431e87b", + "0xb119d39b47e1cdffe5a4f912d7bb19c6e7776803a386ef62226f08ac1fa0f4ad", + "0xf71e68699da769cf06d77c0432ecff635cedbed284256336c682d542e33c96ac", + "0xb600381fb3bbfa7f7c3b555c51999446b148a1507bd6279752e7059e2e0052a9", + "0xa596f51a92c38e63a7172461962834dd700bc847b1e29c03e27b8e49cca19b4a", + "0xee28f2ef5162f0dedb5de1c4b38a1049e98c82a34570b8bfa19735e915909579", + "0x1672d010862647d074aeddc4189a5bc3a128b8084eba6d011046b11ad23f445b", + "0xc99102d4079edccd947367846ae35e2b40ab3f0e1aae8d68885b1753c2c64177", + "0xcd1003ed721269cc94a0736f70714394c191e7a663191268e2daadeeae2790f9", + "0xd33a0f2338d2d95eff701d0bd287e70e0093c6954455bbc63be7de7c39f6d9c1", + "0x97fb57fab06fee889ca8db09d5dad12007024c3dc59f867170d65540d64808e5", + "0xc4a6963627634d4baf5697343904c4c803b2f875f05260602d9ecd45e0e34d4b", + "0xdf3bafd9380eda31fa5f606fa915253ee8b9a41cdbc0ad12e2dc1cbdb76ccf88", + "0x141dc873ceca100df1127451dbd96e117ffefd054076327b68b1c76a4651cb92", + "0xebcfc5da5a1e1882637b9714fd899d59fb957dc9e4afdaac199264e8d29fe5a3", + "0x087504ed686f8e598560ba1f70dbfc72eeefb7cbee9559927a98bbb31b7bb9fd", + "0x767d65e905169fc44546a7c7828c2f1b76898d93df663291db08a0a6f0fd8610", + "0x0ce5170b5c9ec6377c3ef465d986159f12e65c32ad577d8005fba498fc6e5d9f", + "0x76a78e8c3799f93f1359313a6b42f3a4bf92a9c52c7b495b8d82946d471ca965", + "0x1b5006aceffd1d61936cb327b7420f3a88ecc4ed8808db9f245cabdcf3da6fe0", + "0x5588ef96fb1e960284211b143bb5b5a1e431a015b5ae8aad1a86ba1b49fad459", + "0xce5cf0c647eadd6de376cfa52e4a3008bdc0ea3e09beef3be5e2369978bdadf2", + "0x2e55c31e3dec040cbf0f2bd0816bcf6d41ea29376b9958a40bd799c7c788d8d5", + "0x871c62991b05eaece3ac9fd843c62f6a635230d885b075df28f3421c64fafa55", + "0x36004f4df1cd2809aa89c4edfe3ce3df7a70c1cea1b99d906a64491f002b0374", + "0x91ce82776a39a4a32979f0df16d17c4ca0544e7086c90562c66868b588058106", + "0x23d8c25e9c94779c445bc7aa84ed86189617463e3a9e62dd974a23a29eada969", + "0x249e41cd4fd36f6bd6860286bd29945bb98cf66c0348f73b26bb4a28c3103a52", + "0xf158ef6bbe5a54a85f11c58e5c569dc927089ebb641c9d0f6a4641770e551cca", + "0x4e30a215bc185573c02cddf66c40a1c829b86dc639c2eca7e6a4026fd8a27933", + "0x15f0ad2cf92788cf0905aa6f8d092b1350e3f1530f1b06e07ffd8f37d9d867ee", + "0xe7d081057d6d3f5d85be6e28cff22e4f4e2c842d31599885f4e3f1c96049c101", + "0x342f966910c67104f934395a34ffddb97260920bd9043e2f1aace5062520ceee", + "0xd09b337b1ec7e7a3188b2a8981d14dfb89bed807807a6fc40a21ce82a3814b63", + "0x5bfcb62ce51ed31f94636e5e55a4fbbb8d63342818814d9167d690f96ebd9153", + "0x9f46f879a2b469812d61674250053eb01ff91b3d61b66ccffcf8b0566bb0ab0a", + "0x9e7c1698af64e08be4d5d2bc9262a7e0d2ddbab9b5c9499b505245869a1e791e", + "0xddc3dde6d397fdc437fc95bdc98b1bac052a1b4fb1bd81b90d18d63093bc0efc", + "0xb8180ff01aa196209573375a70c590e9ca8fb5eb94c921436c534fa57b93c802", + "0x795c03cb817afda30f993770ee0718774aaec6d0c9c01cb91ab2061539ab7349", + "0x45cbd728f2329f5bca7f7d188e64b9b307eabbd816365d0352e2ea661d923511", + "0xbc1e8acc0b620fe7c44f4d661950981e8388e3f8bbd58546157a1dc716d43414", + "0x4e796ee801061efacf4d84372f6daab14a98c3e91b569a0d580217442e1b821f", + "0xda432dc7a649547983d6ccba6ad31b7cf97ba3c7eebb5eab05f34561759e6090", + "0xd51b0632c736b3403bcc07399e9be4e40e04928ee4e63e87710002b066e462ff", + "0xa3076a6242b4e2e450f227294940d5aaef5b62ad68d63affc390f874958c0c0a", + "0xeed1d4e115039d92f2d82401aa631248a9c5b8de06bcf784adb918ff28d70bd2", + "0x6267b83c5d6acf41cbe965d1bbb8871228a060e46435a29c6a7ade18e44f3ce7", + "0xbe79880b453639b576dfc891353dcabbff3d16479ceb74ad2184a1dc89daadeb", + "0xd5356e5ff86c455718ddc8527750b69b3658c9746c0e47ac93291b039bc2db8a", + "0x079545539cfb2ab2b1d5341de50c6c71b8725cbf3a6ef168869d392c2e6f6ad5", + "0x0e3aaea1d36e3945580c3cd8e26456260a19ec0630476dc3ed444c517baaf5dd", + "0x28d6d2c1e9e4080e0bfdfa907f6d7c9f533cc477a729eb83ffd0f96f6d6f5018", + "0xebb8535debdfb417acf2c13e3ce6107d7e286cc573ec1c06ea28ef4cf3729c8c", + "0x96440ac46648fcf69de403e4c146dd1a37eecea957370f223753ec93f32298e3", + "0xca28ae763d5602c91d437349aebdb5cf9dbc89e6cfc53c124370b67e31fe8f9f", + "0xe27ba10642ff994e47ab1fc5a29ce071d80b82d6f584eaa56ac1a62a771eb21a", + "0x34e7db6c5ad2d646e6791b58ec4d429b8bfd69f76b1d9bf71f860b1faa4911ad", + "0x0b83663dee1daa173203e106ee4996ef147a9940f2318b912364bcfefcb07271", + "0xb164c1d879ad665dafe81929e0db557cf2c573f188cd86fe35ef24271cfd9b26", + "0x6587efc2bfa2ebd6da9816538e3abc5f03c75b5bc425f7e5e9c676f97b1449ff", + "0x668316bbac3e4cfd0c4fee48957d9c8fda962bc556c5ca35246b88b6c43fe291", + "0x264917f66c2d927b7f632744f9dd7b844514af80bf3729f30832b222410a08aa", + "0x38b162eecc129bb2e1c82e65d785e84b5a4a9822bf1e6506492a25b082309bf6", + "0xeaa3ee39cfaed2aca6c1560608bc1a2ae32805812d792471c9c6a9ebc80aabe2", + "0xd7738ea58e4319f98250874c16bc4ba12f2362a8f09a7158ca4e2ecacd954d99", + "0xd26d6e2dd1b62f7086673cf153c3249b15c652a7bd43a4ef7b796dcc2bc44a96", + "0x11a9a4f1f8d10712e79a219eac2fc81b3360c60aaa9bf179e720ec04d69956ec", + "0x6ea3422e40bbd52abd6660d9ee8ff351ccc61d997082e470197a474509ae1383", + "0x1ab854452eb96365cc3ab33c6c45b296864ee61b3a477716dd9eb715529787b6", + "0xeecb8949e3be53db9ab660c71d1bf702d8e919ae6487812181858992e7668c8b", + "0x2a2ee4c33009656e5e38e44a7314c40254639bd46e32cb09eb1b9643d92f9ffb", + "0xa14c5b1993fb968f7e1dfe4ad24716e8d9a5be662cb0af13f43e174e0bc2c5b6", + "0xd5603250b1933ef1cd1b27932627f27599f81ca29c9f02e3627eb621f613c935", + "0xcca950815341af147085a53b4108ce368db1a634f204ad75f3c687b5b7ea3985", + "0xe14e1ce237fe627e8047d5d8f8153157ff1ff3d2650e0e2ba870ece940421470", + "0xfe2bd09d81d1525b996c67fb8407636e06723b4327803a106c19ee60a4a960ce", + "0x995cd4b1f16450b64098fda2e678b14833a3b382f76e14cea71a0d297ade2080", + "0xefbf5dfa769a1e6132741600fa9459b8a1d11b11c3b2563b22c79cddb34b2ff3", + "0x1618c918d173a9af0daf3af314d576a6419456636a46e2c6b8126f2365d627ec", + "0x3b9a773b1c0e16037a429ab42fa3883803f320beb9a34c8e36c80b2b7b814331", + "0xbe3b816b72cc63cdb63e2897064965c1b4e9cbca1ae26c770a20508be1d9695a", + "0xc57f2c5ff8087d6cc2039910f00323a90a0bdc3d2164e0f52bbfa920d62a15a0", + "0x0f11a1d5bf509afb2694bc08584718bb93288355a7bf0f801e478eeb9ef52da2", + "0x829bcd6a2392c957300253f7e9abcba7513908d3013a74387e918dc57053c035", + "0xae4d9d51eabed53e9ba4dcfecfc449bc165b3bd40d87830fe044811f4056580d", + "0x2a8a21d59547009a533695b5808d928992547937478ff9a61fa4388e9afc1147", + "0xcca2ff6985f20f7d1633c35447f45a5b27fd5a93802688e2afe494a2c350917f", + "0x2b8a2080fdcdafddc5d94292ab4fde01d91e7ad2c1b5b5a2f04dca9a729c3778", + "0x211074e33dcaf13183115f89252435b4efd8e1f01c38ed484ca69c14bac72eaf", + "0x5de84a9ddb48a4e2134be8cdd5c274471055234d428daaa1039ae2500e59ebd3", + "0x3f71bb8768f5c8d0db2ec8360278279f4382f3a50a939842d80be98685c536df", + "0xd26b7cf7c6f987f4975010eb9e77becf7c0c93ace9eb53d289beab868ab5c93b", + "0x889d8c294f942cc322a197b4f746c688a15e1692cad0d2d9fa9a125f4998a6d8", + "0x7a0c807c41e209d12128445df83dc1931d5a24923b38d259a7bb9e8e4459db43", + "0x564e904caabec0630b52e056ef9ac08a2fd276ec99d81644bf38dc0b09eaf820", + "0xd5804d2547786f7d26282726d3f9f22e53e0ee2390ff0de8b97c33a4e4237535", + "0x5999f87486f0c721b038c659831c836980a2f7ee9c887c759710d62691588670", + "0x9fa08ce53c180bb53d5352dbb2312603a0c3a5087cb32f81bcff368c457a498b", + "0xb1279296a8cef2e6b80117a48db7291169ad35b39140debe3ee71465b2a93f85", + "0xd3ca8ebe2a6ce6db208b4bf1853b8625e8b12f7792df1ece14f696d1694addbc", + "0x7fea211ff413b60098ababd9e3077f58325ec14d9fe23bbb1cea0025d95896dc", + "0x270f61667793c26f67cbae50931919b8370c0b395704640837d66aba90a393b3", + "0xe81214151eae79a310d5bd8fbee6358a3d897b2b9ed1282337b71a653db17384", + "0x5f2928cf34dc3da9a559ab89d7496510b3c2b5d00d8de730b6df083d03103abd", + "0x619e0c1627f0802d660f1b06bb55c39c0b570514e71c69d5de41232fc6bca961", + "0xc8384233eaacb714f29163a0a9b507b9c9d0ad537dd64c25bce91cec6fa6c2c9", + "0x54c2e035312faaff2fd08fea36df91f2add2ce39298a1987f4286f3592405e8d", + "0x32bd9a7d486a43926319510505b5018b9ae951f4ff0837f8b9058e93bc303876", + "0x6df5062af0c06aed44dc45026fd74a287e15f5a60f122e49343e5a1d10e5f371", + "0x6b3ffbe23bec6a576dcac6756375e6420123ef1eb39750aa6fdce9d874ab790b", + "0xcdd08aecaa642133a9b93ef888c5a808cb2c84c48943e7ffb6d531dfc1c6c205", + "0x563f9e429bcfdaca1ec2ee7007f286b1b3e4358b76d12d94015d9989dd50b2da", + "0xd3a709568bc87059c1a11909396d655c2f107c999bd75ff5104449f53d590f99", + "0xaadf85b25fd426d50f3a22404616141211dbe541eb116b71c6ad960fa453e172", + "0xc411d65a0dd44d5725a401d4a479f9b5aec10334938bbec7ec216572bddd84fb", + "0x4ba49c0fcf4dc7858eb6b77c942b528a349ad39859a2a1f8995593f04ae8cb2e", + "0x17f327906a7bcee96532e912374523b88f474beca4e40661113121ea626a209f", + "0xb0fce9690ee2a2cd55a0e088c0e96fa0e922b4119df230d4b903c4b18cd6da7d", + "0xf7070d16a82595a5782717429dc6d00b1f8abf6778db1836a23954cf66bcdac9", + "0xceae3600d842c5d03611dcdfd0a0231e469c65b151e247b2205cba2916324707", + "0x6d1e0d4ef9387d070ccfb68f0866937d44ffe7d9c4059a21bbb4b0c87b7a6461", + "0xa7c125405cda699de778318152df327cfe79cfbd999c5cc45db15bfed216b146", + "0x5d90e851ff50eff60b8c92d63274c9f9b5efec6efec3c2a412b003693ff014c6", + "0x03efe16b91c66ac58204c952a9cd24818ca61fc6b1ce4d81a31263a7c045aabb", + "0xfe9324d99b23bd0502d6c1020564a850c932a0d010593e1894ae21641ac5c248", + "0x969138be8e3cf0bf9bd0963d2cc6467d8690fc40797bb2c2950de0df1a8329bd", + "0xdefc0e0c25782165aeca15723fb74d95d96d1ed7420be7f3c85ce7cf7440322e", + "0x0bf69f52b595f75df550078e4293602593e3bc2d7ac528b7be44f556435d9e42", + "0x142f2883383cb22f5aa8e863756bcdd154ff3ed8a02d5cde8fc1572db65003bf", + "0x0fc8980e2ce7498656aa9012eff375b6b32216a3142fb974c357a553bc9b248d", + "0x2714b77d484d7639adb40e1158734058dc02dc761c7349c4f3818c70347b348d", + "0xaac5d4ca615ede62571ea4150e4c01cf31dc410b06d5b443267117197e60478a", + "0xcf9f45357d06955730a934d2de24b859d39ac0ac40c8b29430ffe061e3bbe4e7", + "0x52330a846f5d2fbfc26c48a5a95c9d2e3c88a654ee8f5fcb7f9eeed39b2eeb17", + "0x807c2b8bbeec7e6d43bb781f2d7405f9389cef647751c1fd30fccde0f004aaaa", + "0x8cceb13ddcdb1b080075083091120c394a94d6f8f00a2eb4c4498a3b1a7d2f5e", + "0xef770e4d62f2912430987ad5ea59974aae40a81771295fa1380e417b3df88562", + "0x92cefcd22fbf95dbfa766e7c49a4e211596cc6d997976b04efd818ba8ab49bd9", + "0x809d91cdc51f42438312ae27ea1fa94fea4bcb2c9341c274ea18513ba20ec931", + "0x10538534fabee44df110346114f62797a31cf363d4979f881b721043b2250e82", + "0xe252558398e892e85351274a5064fff650f0323f38b0a4456b14a435c9a40d82", + "0x7d313335d362826f589dae0164a04ea681cfcd54f10d8e339ad1978ee63d7045", + "0x713414b22fd3719c42775449895089e6122064b90258a1b495704fbc4677d92c", + "0x748b6883462e9706a8fcab3b0c71272fb0bedac622144d5d81fe710562b8dedd", + "0x2945d4809258eba164089c2a5605a7c9e72759bf615bdce0aacf96ceb7d6ec12", + "0xfd89a991d0e20bc05d484b9cab61d27a0406fa741e0be6c8c1c2655d1310cdf6", + "0xaa296f6295ade70466ed9c185480e186596f2183947f65bd04fb5644dec93d33", + "0x5286efb4ae287693b85bb3eb47ac0b697ceb72ed6331aaaa521d58ad7dcd7974", + "0x6b3a5802ba251becdf8ba2508a6fb1d3db0b5b99646465b12844b8bc23f062fe", + "0xfbeff1e7cea22e0563407d57d9b16518a0355b676c48605adfb13945d7bb2f60", + "0x7c0764932e56518167f4ee45d912c1a7c78f30271b41ee16038f358d9865258b", + "0xfcb998af040eb99c3393b165dbc904a197341fb7dec2d8430346b7772935dfd0", + "0xe46aec72c95d0accef6ebf564e9264de932458fc1143570ddc28eef3801430c0", + "0x08f97afe124dfb2a18c6c624d9d1bac4cdb5dd102da6bec9c676e7fa000a43fb", + "0x50a62c4cd65e3591651f8fa56831ddb37ff966cba87c12d83708aba72591a060", + "0x1abe248da61411aa6b6801dc188e3f834c3fc4600ccef36ad9d7bc517441a61d", + "0x07e38e7dee4062c20f4b3f221df72590c0838619617e051243087250defd0028", + "0xbab90921cb56563d69347a4197b869a8c8bc34d01d753b509eeb5895e77eac81", + "0x620404dca85a89624483cdd7e2b143b12a38b737c0eb783f5e4b378fa1d578e9", + "0xd70f44dbb657c8fcdb90bcc24a47485fdc99c35ce746d8eb2302d529be43bdf0", + "0x425c47cba9cde3b337c451eb7828b0789a76bdbe1f86757292e7c3768cc51b6f", + "0x11b521b56aecb173dff0358678298ac7f55015dd8b217d81dfe52129d03b54b7", + "0x98ef31c7277a68ba0a2a2809f8e7b4099c0e50cd47ab030f5e1c3ea38b92d39e", + "0x82ae5ef4f71c43cc296f1f6279eddc71766102997b0534ca737c0c1a63b0b3cf", + "0x44e2f38922e64f0aaf27607673724e5c91ed5a8176899e935c73c0fc09826355", + "0xa0361b8e3d7709c4a68ff24623856c54d16ead1762578eb562c295f86f453b14", + "0x91726ead73260dcf30eb478615968be91e4e2b6aee522ff6d814bae4aa595c64", + "0x8afe8954bd7cf7a6c40e3903015830f516ecb1a98c1b028b6799e1605892b49c", + "0x142bfadd2c9e988cb07d36bab795231b9582fe424a62de129a24fac85fc47b88", + "0xe369b5c6a44aa592c1d2c55a6fb9e2a0ec3274be74fea84d2e05451db7ff9b8c", + "0x10030041949c30aed18afceb0b6ba1b9bf58c338e275ae6ed9f2dead02a1b6d1", + "0x993fb9325d7be082347cf9361f665efd4d38466de4234233f272b1c09854657a", + "0xe2cdddac0a6984c0ed9d4232d8b58bc2c61e404c3e60b58b90b5876640e8e19d", + "0x4adb2f59c6704b8be33a046a07d2585328465dc1b45a41bf30fba1ebf6b2f7a4", + "0x2c47e906f61c60bbe994a54e3d0a68ba8dfb519928cf350b6cf8421e5c1779f2", + "0x4b712c7fc97f4784b2f562ffad648d29884ce68c505644e2eb4195fc43c00d9a", + "0xcc0fd5276fae744a1ebf274df70eeefbf301ca7e3d1bdd6cbaf425fe0d400758", + "0x0d3e81f37d9d96efad22ded24f6cb7174606c3b2d294259f37e4f754b28fa2e1", + "0xf80aae34aff371a9911fefdcae1289c126c5876b3e4d2477784196a4ed6dc156", + "0x916469d84a13c2971d02ceb166dbe0c9cdaa88c23e57772cd3c260db7d2f9ea0", + "0x37a5b98e5934c2bf16b048f76c06b4ab17945c1f549a20b76ecbf99ac99c3698", + "0x1dcff6c4816141355ac06f9e31a73f31e4b1cd947de42a078eb60ae828d5cf58", + "0x099bbbaa5915dcbc3f975ef1ee1e5da2bf248e96e87856323151a08dee9b5e7d", + "0xd0f2f3478ed51f2454d21cb838b89a3569b3b2704af4d702526af646b42dfbfb", + "0xd906aac9b70f76522bdaff3840df5d9714d352c8a632f44e52370786ab631167", + "0x7f99f6d3add64e2381d7ddbdee1306e6f6749ce116642f993bc4c06970295663", + "0x58988b3a6a1437814e0057b08ce6e86f58cef845f8df78a6ec563d056592b81f", + "0x7476333d9709b4295a63b55e1d5eeb7c23a752c4211bc8696016b70a4c13dadf", + "0xcbe4bdbaa8e34a22b883371b21d5e7cd474368ef0df5ccfeea3edcb377e071ac", + "0xffc358eec58c4c6cc0031562809154edc13b9aa091674e473304682dcc4c2c1e", + "0x9dadda32c544767bd65dce5804a695b8e5c35d2f96c203c350001e5be78c044a", + "0xce2a6bb7c0bceedce3111d97294b8672d0f6ae8eb537ea785398fdf06d6ca2a8", + "0x5ed79a31acc649619c09c9b5df4d595c806a02e93e92c4fdbe882899832f3cff", + "0xb3e94be6cf733ad114b1c1fe76565c448faa9f14131b30aa79aa36a0edc615c8", + "0xeb33e3a43cc5068b611f7a95fef45d5a83d0219906515a9ff02e31c0f1aa4e39", + "0x63a2f545af614f075469aa9f8d4e49fcac12336722936def26c0a4cd01a31b6c", + "0xa6d5ba412c63ff5903e2f30bdf2d1e93118514a8c82b532fda0259b5c3c21986", + "0xd170edc5d55910ee2671397a9b04890faf8b4a3aed0e98852d5732af897989d5", + "0x508e7b9e80ea2fee221ee8a527ded1d480d6bda70d7fcc2d8d521e35c184deec", + "0xeb07494e1b639b09c49b145d51ecb9c2f8b98ea596779d1a14a71e2eed22975d", + "0x50b37a16f4233ba9287fcda52030e2a7d92db254746bc42f7358c9fa0d4e3116", + "0x447b12ecbd02d12bc80c990d632cc2032b72a441b418b74dcbcf3a76ad8d69a0", + "0xdfa6b21b930c94454ae6845041fb6f4e345a11440077c905b4f3386bf4824ecf", + "0x7eab803353df60fa522a5c1c7d155939e1059ce4d6c83d7e4e3ca756bd0fd9d0", + "0x25a35bba3fb585339e9c9df30a10246774a72f47e99fc329d64d36c67df4fa59", + "0x17ca37df7ec4f94f41dfa272553fd5dd3d641a986fa5a9dbb262fc395dac8ea2", + "0x766bb71133abdf384d8c24aac3f663f858bc8283817ec2dc082bb008739eeb37", + "0x5748e015a55f336461f5b1c4bfc80e5d76dcd9bb3c72d583cb7e585c35d67111", + "0x39909825449948d25a7f9346dd07d83f087e50c82e9fe9b9d474c10eb7bab0ea", + "0x441bf9c03a11ecb3b0472fcbed6aff62ce1c44b237c3357a4c3f5e8ca190dba7", + "0xa0456b6f725f0c4cd0917cc463ad42e8d7ac9496cb0c34f067b691d89dc1f2dd", + "0x6571a9b298c2180c91a8436c7ed81da8a69cde6c1a20c78f8327e9cb7bf9d929", + "0x152f3642af6d21630770d739d3f86b5a0a8b8ae5806be4e1b568499349d36882", + "0xa7dad74a9ed1bde34b6cb0baa940b7d565afb8dd6c4e2bb7fabcc30673f255b5", + "0xaa829dc659ea777c2d84851ca7eeaf8fa1e1409df8ec151ff9d9a87bbc8cd848", + "0xf70ff1f39678b75cd7e9c7cf7de35bab6d54997cce23b769ae452cbaf13db690", + "0x2ebfa2e52ab6f68c8b7f2a8d656c1fd997df4cb680ab4106cacbc50c4f1beec7", + "0xdcc2ac91b7ba5294b3de1eb7db158cfa53db057a4956d403732be9aee6899fbb", + "0x65dd99a245b52befb5099b69afa332a9a529bdb5c3b57dedb3fee52bc11ec426", + "0x83f08c95ecaee8710f687b9ef06fdae143fb7581ff1498bce187acdcc1af566e", + "0xffd22fe4049110b55b117b38f5ea4c268737db4fe92b9b5fe691630b39092dfc", + "0x7b4dbfce2925a674b0de23746b226bd901b7aa49140e400af6209e9877d11617", + "0x8c8d8762e05effacf2e70742f9c563458065f7a0abcf9cb6d6f605450aef08bd", + "0xb97df188cf546690574e316b144c8db55c7fee2d241ebfdba1424739b96a4197", + "0x83cb74821c0295a9d11e4be54cb7f2fd4fdcdeecf9a61a3cf7d168338194342e", + "0xc05d2a626a448cd6b89824fc6ceca1b2295361d7e94ce86005f7d527a0bbf10c", + "0xd0fb8122dfe38416a100bbf00ec04cfb14526296e746da85dc4487f2c7597efd", + "0xdef0c26f8ac9f6704a016635dc46386a95e014a8637733f054cbe87e59e0089b", + "0x084e741565cde3ad7fb2c60e0cb1244b1f8f6b87b210993abf2959fd380127c5", + "0x7986d8c793b5bcf0977260c761f5ab4af1d0bdec8a5dc49230f63258f6a0f403", + "0x584a5726cfe9772deb22093a30ff2b9b30ebc018cf861b8aa0fb28bed97ee0ed", + "0xf046b4649898fbdce79870896dbc2752050b5f654faeff320ed2184c7bab659e", + "0x744c1495610301a14d82e49dafbfce8c70f7f78bc0679f2c6fb964a476fcbecf", + "0x4edf5a73306621d2c4966d4aa6cb2935cd9484f1f0ba709ed5d5b91160a5de2c", + "0xb5faa128c00d7f97a3e5204995065e99aebc76706e2e96ccf4b37d7641690045", + "0x729bbb468965401fbb0f2ad1ba0b8ffc7072f9b18ef63e95f47cd98285b89854", + "0x0550d90dc0f6e8d2017e14324d404ec8a200c45ab4a17ac24cb17ca9b68881c2", + "0xdbc02a7ebd9bf74121fa2b4b05c1abf4bdf00a973b43e27faefd095d6cf80390", + "0xabd6ef9629eaafe2d8170d511a39ea29c30d9ca0f071d5ea0b80fcbaffbebabd", + "0xa9dad7abbf1bdbc32d229a67dac65cb5a14cb0e25e8dc57ef7538122471909fb", + "0xd1e063a59f032a9e0ccd9b926bcd6639e197fce15e28dc6b1bb1a4aae5862e75", + "0xe3187db1ce4e4faf95ae3b2136c34ec2ecf028e4054995fa59aacb94d0e16d75", + "0x4aaff2417ccb6bb6be1e3bb759f745053a3d45ce144db0fb627edd9900000276", + "0x3b108aeee36e6fef8d74ec08c937143facb90cfa5616ab7af6d37c00b3a4f1b1", + "0x1ad317c91fcc12235526ed9d6a45c3854f2fd421ce36a2d475fdf92d8afcb524", + "0xe39f66c3ab33ddb0d1fb51b969bacda7ce0f0f53af7012dcd4530435666b7dae", + "0xe071c158a9c418c34270cf27035afb0896251616ebbc57e7e5a2fbd503cb30c2", + "0x41dae0f056d18719bb5568f0893fae512f623e16effae772d35398da2bd8931f", + "0x426b1e3b67fb576cca9a180523ef9979aaf4d14649637f69347ad3876bdb6fee", + "0x2678a16324ded3abcc20b8545ad9a4155184e63b12d1d3a1959fc9d08dda781c", + "0x3f18f5def267a73abf0543135da57d752c57e4344ea941199055a38a46758ae3", + "0x031b50935a859999c1de5b772eb4363615388dbb9d12bfa6ca72111a069052a2", + "0x57bfecf554524f9dee811f59e9ddb287b04338323e4932c44b208913611533ea", + "0x870b1bf5feb1bb5297f34a80b058c81db9c97cb79609c537641f02753f665b68", + "0x3bcadf9d3dba81c74a178f533c75f1e77c64baa6ae274bd25a0cee762da43a26", + "0x38cb775e5437c113ad72ee04f0721390b7b8fdec2efd0eeae24d22cfd3f0abb0", + "0xc6b545762e567ad0fb73b49e3af783035e77e252f5f26fb56c39f2ba280e4937", + "0x922ba1cbae3ae6beb2e01ce1ac2b3a73383aeda9cd3825cdc6206d04fa04cbf4", + "0x9e5899be114b856b51e0de7a25cc1c5c6247fd2c06f0381d5cedb2c26c2081e4", + "0xc7c57fa4cfd2f5d2df0bbca134f48d152d8e284f1c0d946fcab4fdfde9235b22", + "0xf35e4daf46b5b181b6c6adb53f5d2f93acc93b76b422ad82ad996fcf95d5a942", + "0x0cf08cbe8ab573727328f5e5809baba6b8620536053d023dddbb9cc310df3246", + "0x6ab0f99c1cb99bd763af36647d29d6caafafa4aa119ec34527999f992de25104", + "0xa8aabd97d98e4a657980b581b26abbc9280a288301c33c45961f5e35303c31fd", + "0x3c92814886acb8f2af1342904d0b2688be4f269a24849507ffc0a7fd0d67584d", + "0x582781b1bfd4cd4d619ee98db64eed9a11c0562565fedb6613accc03670dc376", + "0x832c0574c81bc58cadbedff928fd08fcf1b4c80d7b323670bf67a29e312f4151", + "0x932b7bd4b919fdd18acaad39228563d65d19da6c7e8737f9c34fa3d58a57c7fe", + "0x153059b97cc89de7bdf0171150670085b850a63125d3e51525bba6765be313b0", + "0xba341526b613c0f3927d604bac37232dcd5ac104bf256720c62ed14ab05370a4", + "0x2b015d7a5486e2023aa93a00261be3ab97d38a1e7e1b14d8878e6c109d28ba51", + "0x554e29e53bd94dfe253d703050d8f54c703a009b412232847fe01e1b138f64db", + "0xfc603400b18e2570276dd994aa168b7ebf2fa9c8a3d7f4edcc8bd6bf5c008109", + "0x66e1cb6677eb595cad35fd028a36aa416f698b6366c73d179b8bd451b0dc3ce2", + "0x9219571b2c2860cfd4bf49b92439dd76304c2b5b2542f036392566b2b431db86", + "0x84c510b2dcbadb5562a66a1312b6016a4d6fdba205b5ee698978f2570fa191a7", + "0x483e7d9599fe6da557b4f5e35bc66b9d4bbaee82e2bcf29aa245c4aa679918e8", + "0x84dddf3ab9a135de68eb188476c23d1460c45c377e9a62c14015b2fcae515d94", + "0xe03b4b8470073abefcb0d4db364624116a046e85be9a28a52bd6fa5b4d2264a9", + "0x342a467e79121dae9c3d203615f772383d1e700fb05ff8ccb5a48961b423ec9e", + "0x025c53fc663b8cb0bae6a439bc0d4f3996618926337bd80797510b67000c6f94", + "0x0ebcae3ede50801c88b517af406fec7a3922b4876f28b3096425d4dec3f2ec1c", + "0xff78e7b509fbac13400ebbb8955bfadc0f35a4b568549dae44b55d15bdcf2a9f", + "0xe19e3bb2648377f25d4a190fc44b14e80b3e2c8dd17427fdcf50451af8b5a8b9", + "0x50ad2c5b2732b7f2c34fc0e2e103884effb23f7672a5290c7a3ee7f7c4609fd7", + "0xe5a000a849f39efb9bfa550d959e86d2d0d92b6f4034a3e75a84a4bcd34b647a", + "0x56fe17466ab440baaa576898ffb22735f4fb680db4ccb9a7dc6f19a060fe81e0", + "0x1ba71ed6a71d823c35464cafd2f4b35a047de73666f0bddc272398d1758b4aa0", + "0x801ac73a07eb37ee08ec538f723dcf2c41ba8f0f0e553c740a73c1b486b29567", + "0x732703b2b96778d10970cd6f90634e8e383d41f3b863c424a71aacdf3d567ef0", + "0x5295a9cea073059a0c806b5d84e95dd449bd15e081ec1543f04709737e12665a", + "0x25aa778a07dfa614a7554d7b00ed9c5360e1bda16df694279d45fb4c0a4ce11f", + "0x3a9e9b5dcda51fd92f8dbd18caa817f53f096f3b66f953a1f731adf41577eef9", + "0x554d4c03a8f7f4b16bee2ecc809a4a03b3e6bec0b0dfeffb45acbeac698241cf", + "0x567f2220013d4694394b054b9f93fd33f9b24d165ec92eb86eb216f2658a35a6", + "0xfec8e9f64cbf08d5dd3e19ecf7e685ac89e8430221f05166f84eed16e6e42935", + "0x1398f794bb66f9bb26eb2a17b32bcff6c6c8796df52e7a5f8c2b58a94e2d8912", + "0xd32d51d30aa51d571b89bea7eadbb3d32792c5971832880053e6da18a6644f77", + "0x753244cb3d3813f9a59b396fff82253ecafa836b37dd4f4572d2ae033142436c", + "0x74077018c89bbebdba21b096ddeb988a40943985fd479a4a95b172d6d89b3f12", + "0x20ca619770c23b5522ee50b629697763767a61f17652ae06f3f30bef7f300605", + "0xbe0de8f6eef387c01546944519c3bd9761c69bce0e9620d9995dfd4dc66e10ec", + "0x101bc2c69d14c170de6b3ecab9208f68b7b1b8ff0001a400f7373b77454113a7", + "0x8cf7437d2474278b2646a71baf196c21994f640e9baf0be059d820f57e0c66cc", + "0xc5751a0d6f9f5b859382b9840127d837a015a7b33d0a775e935ac2f2e57b1488", + "0xbabcfa88c03ae1d413a6165c7dfbf204d5511999a4fc74ea33a694aabb55e054", + "0x66b70f7c56c40e818fb1cb842a13be3e44d864d9ac5f1a4336dd00330685cb80", + "0x790a401845ed04ea517fe4721da3cda115adba8cbd28775e8fdc9bbe7a5bc670", + "0x2696bb11d905914f93ed07d71b99a6a4f2e90aa7bd5eca5e32275bd4870ac95d", + "0x5aea5a8a2b8586491f85ba8a387e6a6cfe8970c5f47a77f517afafef9a009fc1", + "0x7ccf035e2483a2dfdf068c0cbba769d25e21347dbb40302dd2a5c396cc082237", + "0xc7cfd5568e4d9769465645a1726b4889656c99befb7aa02a5b8ef8daaa8b03b4", + "0x5d5436654bf6c9b428c8ad12645ea5d5fe95aeb5e639ec46d4324dd54b0e7e8f", + "0x82f4a8a018dffaa850312b375503a6325562e75786393810a383498e71dc2c6c", + "0xcb82be0f881fb2794314fc3e2cfdd57ea5e12fa7105a824ed033615c4ae22f34", + "0xefe0e3f97c4e6b5be518d58f398ded35421d54c33e0c93e3ca0b527ca50e927b", + "0xe6ebb787f18f2d0d042eec05f2886020262ac20f3c14e8c03885e04136028823", + "0x1e41e40d2da6017775457223d1cdfb06bf41762b8bf47040357592315f627dea", + "0xada6d7bcc5013069b5d0676df3b067110db631d07020104a556b61423a26d3b3", + "0x8e0840f9e38908e49fd12635be7735c7293833c65929d5aeb5035d45942301d0", + "0xb01ea85a249d3c640f5e2ae43b340a5f0455350719b13cc8bf1a50c8941a8ad3", + "0x4e5524cca029a2729d27a3d2a712979f35e07cf9d436ca7dfc8c414eaf5d5a52", + "0x72208f2a837b1fe9e7445a72f7d57e414db9e0bd87f7e5daa8de14752a2a87c2", + "0xfcf05de95385cee5449c1ffc63cff009b585ea5896b2ad96d43e747bed4aec71", + "0xb9ba0e212f4dc38e5a0c2b62bd0c1bda73459e72233ea38cae8f4a91f0a6d764", + "0xa8946ec7aef9ee1402442e7cfb4363df529b915c96698110ec1e3c15d445299d", + "0x07349344fc0ac74be9acd9f616a08b3d1bcfbb08c2f95d079c235f52a0322fcc", + "0x223fbcf9b3a552057f728cc0e4dd283176735668beabba942f62006a902249b3", + "0x0e28903b5f5db741536eaff97cca54fb98357ffa69b84bbe365312d77374e47e", + "0x6588620c06642d029a1f8dc0b95a14c198b7af2648acdf1c8d73c80d87cdfda6", + "0x021f66c28e1fc7678af4f05726d8670bd88a143e749a96edcf7cf8239607e9f3", + "0x339ca9feebab8de1672036fe85ef63bb591f5466da074793436a07919a7740d5", + "0xee504bd35830555e345301e108b4ee3698c9a0739a8d4210ac975f1aebdfb944", + "0x61a020081cf4fd487b0e9b694465b94016d461897d4e18990e2d34a26e6a6541", + "0x539ed46b92da9b97e94f0bb089b034af3e6c85b9812f5ac18b64026906ef4f33", + "0xd244eaa108e905c9160cba9f75b3d4f46e428749c0a2de7b165e95992e63caf5", + "0xd88315191813c72230222f636d5de64bf5ed7bbeefd574b1d0c2fa61e16d5919", + "0xd428f280ca6b4ba0ff8bdc58a1b5c8ca5991f9d2dcb8522b17b3c09647a9c8a4", + "0x0744cac8522e0366aa470de75f1dc92e307f82e2e300fac1f8adb359e7f5d9ef", + "0x6e504ac6d3e63679f9db61c3e37bc9416d687156354d1fe82f7f434c4e43708a", + "0x34a5c50161a9b06229234a23f210458cf64fbb5531298735826599bc46179e87", + "0x03a94978bc793490395c9cbbae9fba0f7a725f5de2737e65f7e4f55763631585", + "0x183f981a62d19c3a4edc055aba9d04e642f89f203381a4e63ed7496ba7a1074c", + "0xae5492ed7758112e7fd6c21582df31dfe0b1d6b3fcece4d2e8bcb7d5bf56a00d", + "0x8f5062a00e823791785fac6c0eb7c1aa732a70cb1fdd23d4336b2323a99d9f5e", + "0x74584b0ad5236526dc1a4dce8f4e65f87ba3d529749a702217f46ac9a6acef7d", + "0x06f7debcf4fa4511a1182d992fb0ee265714e70354a08fd496f2550a9e268b70", + "0x8a6b101b16aefa233c158e0392f0897efea6b4144a98ab3050551f8f530ffc94", + "0xc65d3267fb49c21636e1cee8d39c515b9028058e8b9e0ec5c2d6e7adc9293e45", + "0xb9286dcd79def251104af4f3b68de8b9e5717ad5275665b6c079f49afab7770d", + "0xfa735c22373482f2017aea5547def3bbe6f0b8f08b7054b775dcd04c3a3b8597", + "0xe3d360213b5720573864c6a1d9204151491bad4235e56c35b340b22e8c29f5c4", + "0x87ee0e69a5db0bf2dcf753e2e1dccb92725f9d1d597aae378743038121071b9b", + "0xda446379789bac65f03bc0d2bddf4da37867069743ddbc4f2e846c90db8ab568", + "0x553465cc2c0ab7fca15424c8507d216d95f53a25e053657fd8eaad58efd45346", + "0xd3eda9aac5e84aa9cb845cbbaa180854868f497fb79c5053becd16f6d9ff13a2", + "0xf8bff7af9e6fc79a875bd7cc4c1a0a347572c5f83c8a6864313e5f182a8bcd96", + "0x3d49c1fc6ca0f9eef57064a5c125c24464d1ae6f29989f2e7f4942c50128f31c", + "0xfa78f4fff3ce4c2b34935ec5692b9bb0f326a65ec6b14299f990e697ceb89a34", + "0xd7de5c5c86a0ada9f328e8b67393f4002714c5d5f0d1ff68129c68b882d5c6d3", + "0x89f2fb3dc33363365ce597c0e5c4d14c0f66b60a8c6ae3f6d45d72af301cef4b", + "0x218c78843492895ae5ab088ed80e82eed320cb6f98595a211340b19428d94985", + "0x5380a88382839684d7d9fc7047eca486ec0383966b262e5e46b9aa0859ec1547", + "0x19cc514f0518b3cedbe31529f8506fe60c93122cc7da49ac6f855abdbb729e9d", + "0x93e2d29e5c272ba8dd20f621132c60ddeea7779309a4d14fde6e6d58b0643f98", + "0x3158d8140bea277184329a7f2f2bd7e956725dc80573ccb169c40bfdcf3f2c70", + "0x7505475407491f5a4619a25b97b1d5cc1e1ba38764be61b694b6c1d3ca51c216", + "0x40c556618336fdf46e581183b7e57a2f6e2665d8adf0d44b39d9a5150a56a13c", + "0xe8d0da5275d1280e0906d62421bd64a3397b5fade799a701e0a37111d13602f9", + "0x28fc4cfdd74093fc78d100681b770d7aead4d57552c9366d56bee6e7776653ce", + "0x81eb57db158fe3ae4e62785ab8a5c4fa07bf5a7815f1dcbe25bb997b89392691", + "0x361a76dda932befd901f101507a238fe1dd43ab563d09e5e3c6ca43e6de48f19", + "0x9931f8e7444209b91572ff581bee13380706e6d6de933ad96f185f71eeb1ca9f", + "0xf9b544a3c3090c494eda2657a527288e48b5f4c9032ed26eba3dcc6558508d79", + "0x16936de0caabfbe2e395583091c3b90305cd1fa734a5ecacc8db0ba8c40fa89b", + "0x998492ec7efc0178ad691288e36758800b139a26aa493bef28f2e96c460be402", + "0x626f93a0163fffb94c1cf227e6a0ef125f7930f3a21971dd3a9d443162a0b176", + "0x64a7ed78b519d1f04b5b3b46c6804c0647889475d4fe991c8a7e405a4a2334f8", + "0x4aedeb8f7470b9c97c2fec75b4c44252a855d36b73c6a4550d15368dbee9d0c2", + "0x17095f0fb67a7d24910c086ad4cac72e493bd7276afef463d05e26fec1f39381", + "0x05b5fc483f428f6c854403b365ad11f4cb170a2229ec66ba671b94ed50686858", + "0x76bd14300b8f845500b2991e8a78df8dca3054016ef6b5069b77ef05b806caab", + "0x718aafdcb0223d7010c9b6c012c38aaf82e02c8737488bace9a7f8db15c60047", + "0xeedb09efda45baaa9034a18259f1aae1275a86fc81a218d9982e0b17f8d743fe", + "0x2608c1d2f2f8513ff1ac6e33d21659624c16e00218f984ce971eb9bee4825101", + "0xa5331637e8221d95d318cd4f32d5d37698731f569c4879c151086974e2cd4e95", + "0x2f9faaf3d219ae56a94fb4ab12f940d63a1d86776aef6541d40ed63d03d44682", + "0xd91d881ed4fd4b6197d77713d2c42758d9e550343ab67fd761c7c3e7a3724fe5", + "0x2404c00a40e6d67254ded0ff5a5413776351b8587ab1fdc4cde74c316435a9df", + "0x8591c719906e31c2150ff3183382f665f756c9a324ed470d0916deaddc240edf", + "0x8d670ef59f2237573019bb60cceb14f7198698773b572f6acfe3d3bad6c33d3a", + "0x3631004b8ebaff324494cc7320bb3c0deca53480a21d2d7fdd516271af7c97ab", + "0x4dc37b8650d5e49ab1a87a9e88912a98a60099cdcfc33a5c21627f0bb05ff124", + "0x95a8b189f6335a7d1523c470aedd2fd41f66043019da9a1ea46b379372ad4d6b", + "0x7219f1318a197b919a83dd51417803c752c24dc3a005cd08c091b0339634e806", + "0x81d850ed79388dd32265463d5283b02b11f012e08eddddfe389d55790cd1ff52", + "0xe170a7511431b18ed7330446569a253047f4fd0653915e107104380ac40affb3", + "0x9c38e78c1851edfe159971a455a70e1d61b1ae71ea0cf828ef40d4166741fad4", + "0x1ee49fd6dc9fc4f6e8bdee61a3e010a0eb9d06597c256200538ec73580c42bec", + "0xc0e415e7a108c241fe301e6ac22c81738470b499aa65317fb9344d59ed763a09", + "0x4fd8e4224aa6f2863ec655c10e829e113882646230a3dc753d80c111093ba167", + "0xcd7f4ab1b3900488068618eca688f2fbf85e58b265ec0dc4c0ea55f8892be5a4", + "0xddcfaa6613a08b4d0a5f4259abae86cbe95fd5ec5ec77fad9ff2c5662efd4025", + "0xf8bc573cc98ace8805ea12e49c664d8d9cccd650038a4c6ad559780d1d2392cc", + "0x65d8dda84a5666f872d4a744a7c98b6dac5e1082e06625d49d32212a37bb1291", + "0xffc19cd76943cd82bd7aeb8d25d6dc4bb71f834cafe50a5ca2ae257d76a93db2", + "0x3efd8f977e31b77007ae5f290a63c22cb417b52d9bb36899baf826049d169785", + "0x9e225513beea6034be1edc5c51a3f12718713578a2a69bafe7af00bdfc477d02", + "0xebd0e1994eb8dc81f7ed767aa664ee7fc0514caf96fa5080c1ea120bc9803ce0", + "0x340d016d76c6f0db1853ef43ac32c2ebcca11a936c19f0f6814d64aff8d63ee7", + "0x57ba48a6bee7cc99a609f5ec1c9dc2aa1ad26589b292d0e913bbb7e6700895f4", + "0xb0b3f638edd6161784754f858844e77f9dc13a52943120680171deba7272e9d4", + "0x901ab47b6f412b57c0b09020defa44b5375c4e80749199c084f6ce0ecb89ab0c", + "0xb8c7e255dffdbe496fa3d949ed552e36ebdd6d14acc4049491ec6a033c086616", + "0x2f1780d8df1d77937e6dc3ec8483d39e3722eee9b102d84cb412b6765e1de74e", + "0x88712075a1174fbe444b715cad257d16547968ac267142ef293897332feac87f", + "0x6287016bcf8d4d4b67412f0b94b080d6b09b35d2a969d333fec03a114f49852e", + "0x7bae2942ac4d0c98a53feae67f5486415b9a59305b6adb3c7fdbf78642004eb7", + "0xe2dcb80caabe834c0897449540154ba5102f7c7a534c40f05dad77d42e26c571", + "0xc1856ba8c970c81104603472986449d9a6ad17f56cf67dd243c707c689e2faf0", + "0x3dc698054a61289b4b39f89c7f09dfdcf72c4a5a54276e508b095374cbbdf69a", + "0x0b27594e85ee0393ee4b16ac44751b9a1d8a9e7d704f42cc7c46bc1cb30dda54", + "0xfd47f64c6b87363b36d1de4950c72a7ae7fc61d941d6d046f89eec5bd63d863d", + "0xa420ed29af2c8a076098250c29704638476fd014558fdc68524d7a1c9b4043cc", + "0x20e516a19060c4f95559484dcff148812ba875407c1e0548714b630439985f65", + "0x779971f5b6aeb2717e6e336f91b3babc6b96621fd93352f0444b96043c34c1f0", + "0xa57891c1f318ee62a989a151b1be70176b8a86466e3f1669a87b3131b3f7190f", + "0xb09340884952e9fce0a543bd8e98617caacc63d4c87485da51cdb906aefd479f", + "0xff2c2dc309fb4c05b90a80a3fc715427f540be0788b33525a5ea00f0376ecf8a", + "0xaff098da407d643f4d60ce864184a0cd2001301833da3f379659ea4462da737d", + "0x0650daa5593e4d835532cb1cc353bd2e93fbae818e1f334491539ef4d363c2c0", + "0x1d6e8666bf720b0cc1fab78d8ea982ecd2b44305dac8edb2f5b9b9881722399a", + "0x673a6337982d71675566e7e6acf703f9c5b18718856bfae59edac11f4cf2ad3a", + "0x6f68da412db64aea7ea33dd930e145f45b36cce9d285ccb878d33594bceafdb7", + "0xbcc221ad6460fb4066d8c630720a519557064183fd3ff3feffce36b0d80b8da3", + "0xe671736f8a72834e5944c296f64a0d35743edaf75becef65203409bd369615d0", + "0xe4b50f9724453eef07bb00e56c32f6e056bddf38fed0cc80d3d9d2f86258810e", + "0xfa0594818b10ecd7be1deba272ee23b3cce007382e6974645a505df7d289ca39", + "0x35cfa0772e326b9325a613ac39ec350241a62d9f51cc0a1b0de4875d2ce48059", + "0xb6575e9d2cdfe08b13fce3badd0a991f5d331cff4444821932973767677ca13a", + "0xd4821ae28196784c066b254b2fb3b6230d5f4a0e769d8b85fc7ba453d3e93f1a", + "0xfd9f762aa42f1eb43e75c3c00d292f9afe393a14e272037d072872f820e710a8", + "0x9f78b0168eddb9acecaeb07fea85080fb638aca8874a9ff15d3bca7e3e4f9e73", + "0x8c2f0986e10039e96cc6695884401991d714adf5c445e9bb0f90a14cb4a12477", + "0xb7059b47417c4a06bcb11c6ab78221835279d130f52f5cfc316e80f87977b87a", + "0x56fcf49a8a7a69d4f8604190d79eae7e2957a1d129cfd813c9447249ace909fa", + "0x9401c3ee1a458253b3da8820c1e9b8945710839a826582f3a86e8a482dbca0f2", + "0xff3f9e5724cfbba1f837a10f3844cd437c0f1b5e31ddf61c4e56ec504fafac55", + "0x8787e48bce6ac94e62c13b82c544c2265b42ec08d959c18722c3f722cc8ff2d4", + "0x18cfd60669f16555f0b0a429a6b98bb64e936863d5524e23c3d263dce8177866", + "0x33a19dfb8f79494cd2b1b5983884992f637099735e67b2197ff48b00fa28ed0a", + "0xcd2d1272d5982579a63f951c3678c5ec440dcbdc2a5fe4ee8c765fd16ff07353", + "0xa7bd53be4ffe9029d5bf0c15051158d4fa1d9857861d9b3fa63dd0d5052ac98f", + "0x6e039f06324bfe8b9604711719bee4c840526f8c3ce9bc5c3c5584315362f0e8", + "0xafd6c666917c47aeae4d44703d891a3498211054535295178903665328d24291", + "0xc0f0dcd1102ae4eb932e3ee71f1417dc852f3f1ba6a8f2b12cfd8b86ceb3c335", + "0x6845dbf33355716e4161cd3e03ad296a4bec741255d91bcdb6e36950c7010c75", + "0x832d9dfedc39989c97066d465f256ceb8922f99e01c730865f62bba3a0aa786b", + "0xdf99981c644b3dd68728aa9801d5ae41579cc499a534d5213797b3fc35bb64d6", + "0x0115b4118eaf40a9f0fc1cdb8f26ed41e88de8c99e572f72d79dffad9a776e20", + "0x8b09da2fa7f9bd66158c3d469cd22d3f8ade9d86d0cfbc49ccb48335eac53df1", + "0x1edaf123ca42fef6c94b334ab5584c2837949abf3b3ad0b05de4e1da16a5fdeb", + "0xa7bf42422c1beb01742120f78b46ac1eae595f442b0f246f53a919343b4b3b37", + "0xcf72cd32a99afad5a5d9db18819131219a92c1404b57f64215a9e9e891c9428c", + "0xb0d4dc75898e16ff0c4b8bb317669eaba1e54f8d1a1cced03e6565ef3063fde2", + "0xde58a7597969ab3d35a4269d0623ba77254fa140ee0dc00f85e1d256f515c26c", + "0xc0c52dbd3d1febd3e5b4a59d9a6c1a4023d612160bccc5733f2006738baeb149", + "0x42759b763eef5c70b4fbb982cd247c29b0570dcfcba9649084622bc93bf0440b", + "0x02b44944241f25120667fd0c83797915239018c4b370378719be93a809d0fe31", + "0x176572271efc8297d8472120996569abdc26e0b2695cc1c69f72d8e27fd927d6", + "0x2c2f7eb653428b3a5329b339094caeeb4924d322b4362546ade060f8ce89ba65", + "0x3b548a06156cb3ce3ee222cd68a95ecd69bce5914a699b020f0890719a5f97fe", + "0xe74cc69a92f69d63c16a4baae5cb692e57f2e2efde17e0c46fc4cb338eadf215", + "0x9b2a05590bcfc56c432ddda8607b7902c1195ac72fae25c7f18e547a20fc1654", + "0x52f4d9509d31fa6100c7fa8d644dbfe2122581e8a277c6fe80b68aec78035ce6", + "0x380c3d4f876907fed243d11a8caf436445ac5ec55bfb3596091791bfd80ab8d3", + "0x386a91979bb6c8e42d282132f6903e517204fb9ce1379fe5f2a90b66b1f708c2", + "0x333d6dcba02dc8418059d95d43e22b9bc33539bda31f424c762c5b62676b04b1", + "0x50b91f399926865ca64ef68dfa540257f660759ced1276da1033bba6c2500da1", + "0x78266d3dde661a3b10f43d31375993ff002a7b49717a075c6367259baaa58fb1", + "0xb5066418dc050952f056082ef0f6d6b47fb423e9e5458176296aaa6edb7e7af0", + "0x3b8320734691ac54f2a231b296b889c9c8511843984a9ad80f3c43b8fdfb2391", + "0xf85d88e47f6b7ec5f5107785f80fd8b51ad6d5488be9b147e03b5697567ec932", + "0xc352f5af398bf837c89b42eeb48c322ac8eafdbf57af5c1c33ab0880623c7599", + "0x450a16022ae364a4f3ffd5e554aa9a324ebf8b9e6e635e753f4872ed6f63051f", + "0x0438e9c7d357912b1527a7fa5d6e30ec428e472a617d30e33946e47a1f648dac", + "0x4cea137040ab131bca9c9cdd41760c181244c736ca1d9d19370012935de0de6f", + "0x757728e69874bcea324af5348e76db22c3eaf8348843f61b82271570a5a96a93", + "0xf45bd1383b1d53e95dd4d0515f20035cecfb0707442c525c0f06b55e7ff52a61", + "0x156d898c3abc5a55d9860d679d97c36b2e48f833c1342bbf455b13874743b1bd", + "0xb3e80e0fe51cd1ab5d8ef201a011bf6a0a5878d070eabfdb4a6d7c77d06dfbe2", + "0xf7302c6bf2d04fff65957a18c8b988bf0e3dd7f3933229a6f1700e6447560d5f", + "0x73ad7c6627035301b9815d82626fe958ec0ddae62060f0c7f431865dc1b9f6b5", + "0xf10b5b53d915c1bb2eb9979dabd2e917dfbcfff17f1d09cb616b2a5179014ce8", + "0x93570e1d259272e6b825a5ed34a103fffcd27f792fb5b09a2442cc83a3bd172a", + "0x8c5538b8ff9ce17625018bc309af258e4f5acbf9c7c2d4e948cee8b30b336f92", + "0xe9a6cf047c81e965dd32c57edb680c2aaffe2e367976eaf49da5dfc957fac2f4", + "0x7b972cd72e31473b463ae197e30b121afdda7878b9bf0e1961d3813734663435", + "0x9690c83cc3a1b1b8649a89a9bb1e1fed606e4c949d46ab8f66f16c8320ff5127", + "0x8326afe249d762313e5e089474f174825821099a5e9136a7783c3eca7a160d05", + "0x240410eba6ddc5b60a0bdc83fca59d615d95c64b0afaed0a7f17b96ea65684b4", + "0xc48969c8f72df638ef2e8b98f0d67dc6d9eae1d5bfba2b02e584ac2dab51f3e9", + "0xc4fa4b201243c20d35df1ba466a4c40139471ec86bb55f872d54f6404d4bb05b", + "0xfd085495f4ebfd30f7ee4ad9f122ff3da3932fb87ee9aeaa7c8103602671b96d", + "0x17ef24bb8c605d6a27b3b44744353bfcd127488be96ac77a3d8c706015889040", + "0xfc2fa0f9a8aabf5fe515cb5b4a4ec6047f08e6140e81bab8624b02dbb3f99d71", + "0x3ea5269fd6cc38f8a2167cb1619d643ec9de3deb9ece6c19542626f7284b7b0d", + "0xd6ae9f4d8afee0374c662a86b489c603b4e16fb82dee3f57ddfa971e68531fbc", + "0xcdc97e7c7aba5cc5939382dc37142c9d59683d3188d642009edb492ccba1ed77", + "0x847886244064d2edd6791561638b63acd09336b4c88eecdb09493e8ee5324dc6", + "0xc3695209de581e3e3ee849b3dfd93d8102c9e047edc1596aa332d8137d0bc3e4", + "0xdf1399b24c2588519cbf19e2e7218d0fdd793cae1eeec52f9fda7c729a0d4e02", + "0x9718b2e289feaf4a2acff80f5d1c61400e917200fe97b5b9b6200d4c48e7a526", + "0x6fbd4948fdbf124fd0a9c958953abf372854bb6e29adfe9190b0101b97a342f9", + "0xb54aabb1fcf4c8b701734624fb68c842b500f5e71c39dd04083f8a3eaf7d7c51", + "0xc3710e6042dce5652464ec6891ec5b9f436208c12afeda5fd3f454edd8b5d79b", + "0xb6e3d629f78477b79bb9368f1611b62d717b3214675c64f1eb737dfa17b03b04", + "0x6fa6a372c4f33ec50d56a4039c0eb7d6652fb934b24ca468c1fa8f034aa043f5", + "0x51ae789b75938201ca463bd4e7c36152aeafc4b63544293e689e15ec80ed94a2", + "0x4fbb4816f1ea23ea83a425969223be5b9cbcd69113ff43e622ba9753a3a25901", + "0xb2df755c413220d7b684b063f81a800072f2f552e37fc057f555b252f70e858d", + "0xb5d01c218c65539914a8b01c40bcf629c83bbb4ef0c6289c127f583b133aa4ca", + "0x1489588ee88b1922296b2ba27abdd0d393a774009941f4779a63d3ccc015c34f", + "0x093c64995cd670084f2a908be6d6a508f490641474bfc722eb8605e0600d246e", + "0x9addbfaec75c7de95a89691ac3c23ac7c557c278155ba6026aa73258870847f4", + "0xedc09711042a5e3e94a1fff57e7d00e0193f1fb292e3625c6c230c66bfadf1e5", + "0x99a935be3a0aeeef44a096fc8b18ed302bf7224b6b7abcf5e897ad7fd44ad9b5", + "0x00d804cff32284e41ec123482da2a86019ad97d8b36c0b743873438e0cc7438d", + "0x669eabe8771c02c109a7d46ec07b346be0901078f5cd4419b05f3f62442f0b1d", + "0xbf26eeacefd0dfe498f7672aab4a5262d75af5a7e8f5a5dbf0a3e9cce3573b1e", + "0x9b43c92edad88c5825f99c8490b627b68602701a8358baa22103d2b11ced2d77", + "0x8e8988d54263d7e2d9692ad884d44575d3fcb7bfc37333a814ad19328df3c690", + "0x208d671c12795da6d89847247d9707cade2dad378a840d281a637e2af57d137e", + "0xfe83e593e005e58c47571fa00075d440286e02674394eb46956a25ac2e2d3622", + "0x41a61f08717bcb479edd0ed1fc9d8bcc13b9ca3ddede35dee6e0d0fc347c4c45", + "0xe9fa1018977dcb1a2dd82ef24105dcd827bf815985fa97afc0d294711997f686", + "0x7deebf65689aefa15a6d15f7b6e4818547f56b01f622f94fbc42ef968762ebf4", + "0xa8fad9841e2c5d7c1131c16145c57829f22b62566b5a7366d6653157dbddbc83", + "0xe0614431ceb7f622aa4f43c03fae28318aa726dc1ce0f95d69aae011eb62a017", + "0xc731b4b0341ff915979233c76f3562e8a28f268b5d3e531d8d4f3c927837f123", + "0xef265f9c899303b2afcf6cf2418cfc8f9c71d4254321aed87827c0c749f51546", + "0x40edf9f2122834fdbfa84483c376a6bd8b67eac8b8bf454258dc7810f8e849e9", + "0x63093778c25926c58ca06014bd1b668b4d89e641acbb94bcb13c9ca0519fdcc6", + "0xd8b930be700bbc53228755ecb5074d16308f8dd3187cd3a0efe0c2414001458e", + "0x75b0cc9e892313d9c1ecfef4bf4b3f5976f1c830a7283a82fc331277a4a97eb8", + "0x4f102fe9ed705a74524f2598ee6f22adfc1d4186e2ebcc7331bb48e900795cdd", + "0x48a257229a0e91207cc31ef82f00532ec12cb605abaeabf1235676dc57306357", + "0x9ed64622acf60a2bc6fdb2bb738517aae8dad9cffa738ed4f95b02d3be04176d", + "0xd23a29871a1fe22d8bb7e7749aadf6d77c0140fd383cab55b099714af617a371", + "0x79c92002b2077df1c7127a4c49575d7caea872ca1175011d47e019ff14ed8535", + "0x9a9434b2d09c181c7d9f8ce3e10fafd8c3b1e27b807243d74075e747570fb391", + "0xd3c946d1ed5a76e6e17a599d06bbae3319dedfa047e8443f9df3c4b09aca3b88", + "0x4f3ef090e35ccddb78598a581800ea6f2709c691e3024c2f5d13635d3fdba27a", + "0xf958691aa9a520e8e399e466e390679ce3781b82f1d0dfc62bb53d0b3f640176", + "0x4a7faaf47a5031e0a798dc00ab8248aae16866cf74935ac0703d3f1730617d79", + "0x6d350d1568d7db6c029f32d7c8fe47cfbe99984d5d24a9470800f4b70207f653", + "0x6553729bf5616496d7d3ff3e120ddf1c4e64857c53303003e4c9c2546f8458c2", + "0x05491ee727fefe4ef46b9922856a5282c4ba27a90150b967a04e62d85cb3dce4", + "0x61e8d5b5143c0c532adc9f466dde430afc1d1755775dd39095a51a5aa2d4422a", + "0x2b5a26a051940c5f804db637ca77a835ab50f6c62648adb5da7ea05b39b04b38", + "0xbc391789c853b9ed06fa0d2533f1f56b55937a49061f1cd1a102fbe88657a764", + "0x4206d3b075128ba118dd06bc273999ab5ec097f1690c2abfdab351efcfddaca8", + "0x78075f3f0420f490386d419fe2b187d8c6a3b0bbb2158e0f9bf04071ad235bb2", + "0x0cf1b70db8922a59f11eb3a40ee22264a231905cabcb270dc3df11d22c8cb780", + "0x093ca9659cfc716634b38ccc97cb8193baabdb6569f4b33e56e3d8ec1157dbbb", + "0xfb17fcc35e7609264b5dde51f3847f98f555808c693fc345e6b4da55dec4fb9e", + "0x7b6ff07aa7bd0decf690c24d510a2d08ec07fa78276311205004b521f15a161f", + "0x2323e4a69094bff2e821a49caf30ccd91717bfd9eef8609331621071d4fcca2d", + "0x9f05738aa053bd67194099d8bf7baf3166c4cc18b76aaf7cc1a197d4af911765", + "0xb46c0513fd784d2ef4029eb3352e4423d627861431c160988bdc901c01957114", + "0xe1b5c3328267a91718980d369530a7ce76c6f7e404030e880c5dd57508abb03e", + "0x66d908a91b10d229bd478a9ae64021757a22edebf9d41e4a58e2168e8445d137", + "0x9de8289dc95f5b61cdea4a32c7064750adaf034123380c68251df2b816790893", + "0xf9de4b4229f1d152bf4d00864d8d5c37e8cf38264b2fe304931392f848fbe8ec", + "0x356654f74408077036e642d6ae98d7d3893190f6169110c682bbeffad006e33c", + "0xe5a731364889cf98c6136de7365fc53b3f8cd622ae7480fc35a9aca8f4e615fc", + "0xecb8b7af4e850d0854aacb48f034d96a74a81e588f900e1acf0f421e8ea2e59a", + "0xd3d25554bfe2217268be1364b9f74ece500ba8e49260218f22bdb86558edeea2", + "0xee9e40ade104510e4d04b9141864d3d14ddf947f7dd8d05ca29fb3b4d4efce7c", + "0x440556c74bba8c2d89d9eae72f713529f0694a72a741951d5144f7f977ba745a", + "0xfb63981c40b30bc7675280cec8063e7f64f44eb60d983b462cbfbd923a18c46f", + "0x666810ed3d8a8cb87b9d3d908285b7428380cbffeaa2ac9f50b9acfe3727a334", + "0xf87e07add78b9ff8c8b8c4e81b1086ae1fc9f97903a0b8d5e30442781abf5747", + "0x7919d5dac99d403b646b4785205c3ec2635023816e534eef9f14aefe01a0c8e5", + "0x99e1935acf142897bcc10ca07285849b08e5d8f0f8ab73bfb5b6f79cc65f4919", + "0x182a38b8286b5a6ecc93b431a20356a178cba89ac025327e28b7bab5e815ae1c", + "0x666d49f7785b6d3db5ad92f1403e2e447743351bfd31f1b3b61665a79037a1ea", + "0x898f91e453cb2349f6a222f23aa848bf01426fb40fc7c00fea3015847da37edd", + "0x6919143735b6f85a9d0e37bc4b81a92d664b5ef75e40be58fd1b269f5627fdc1", + "0xb854a51fa1ac7182b6f92d48cd90d8a576e75f769147916565acfbeeead664e7", + "0x3924e051c439c6a49a7af5d5a07d3b9b193eb1a6c1511ef406aee6f8e94b2f94", + "0xc6a2b425de2dd99bfa1c0970d29df185045a56cfd3b4021a3eaf630d4e87c6b4", + "0xe66104bd368ce9ce40920c6a0a1dace7be55bdd5ec594cb9169e42cf07282168", + "0x876d3a55345136b3079c260cdb312f8c708f9ae8afd3a6a90f8c018ba58be19b", + "0x5c0849ab35f38121bfceec816cdcc2525e88530ad874086bfa4056c8dedb1e77", + "0x3028fba2e7ff0c70acf12e33e744a881b94865d6f373a50d5d5eef23202568f5", + "0x9ac128a61197f07dd4b0fbac847b864a6496f1047a085e9a26f1618e9ae83406", + "0x55c9428bc7a5017957e025f046231005d4199c8e92627074923280cc3f6e3cc1", + "0x474004a4237145fc86484ae385c21523250746c662e791d5c366441c3b9d2189", + "0xa53ff4b977a2417289f9b532a20b758a2b0bbaa84e71c82e783c7ef045f16eec", + "0x0418a7320d961c5dede728027a0be6f4979211ec600b564edd408a1e9377d5b9", + "0xc8ed8d992f2b75be024eafc15ff7d7c5c0b0599cdd6b603437b4e737a60993e8", + "0x3f5e222d8b28b38a8c7b190448286d4a14afd1b0cb2697fd9356158f5215a0e8", + "0xcfc961c5ff3b8615089248635d900fbea9ae9f7b4d86ebee3703453567a67485", + "0xcb88b820997d440812da7192f44c6debd27da3a41f04b595f515e65cf8a80d50", + "0xce6f3fa44aa7a177ea56d97f00ba344852f614951e665e3742429a6aaf698c37", + "0x21bee516e3366f5770dc4f6af36f799a5c967eaf398f2b70c3a8ca694096a9d5", + "0xe5a99d3fddf70f8e732f7f4a14fe81de8cf1ffc945a47d9eaa7a28c5eaf6ef84", + "0x0d9e3847d768c28783981c6313bc9b0e60ef4c2a8693cc25d399e9db50f393da", + "0x728f54d10e7b531b464ec7e86f3320a4e853967c14061e727f58ee3c115cf65b", + "0xe1a0b9237f50b5e47c98190589842f0b72be2acb3b6a57c075b0c9933ee23100", + "0xe42d8bd36eb740ad018367109ad59f8a74ab017eee893be54a4d1b04b7cf37f7", + "0x72bd0949deab66aed968e725b4434158741c70007855105dd17ab9cec2cb1a91", + "0x450177933434050053831a4c162399286a92c18f9899c413d1c579ef95379fad", + "0x8c950374baef552acae9affd2238ce1b0a1c27e463a03e518b99f23f269f805c", + "0x7a09cddc74127b837571d245fc039489cd0be46ef3618460013de17c2bbf7933", + "0x6ad520bcc098e50d371d504a1e4f4e37b63484066606556debb14c1341880f9d", + "0xca0232939f26cf17430ab3299f1970c334f8dc132724c6e6b01a2467f4dbc149", + "0x9edc115c3af74d77a3267df2b87a77d82b825d07993b74fc31668a09915ae070", + "0x0147d798bf772842ca2e33c28b65fc6934e41e8775b5cfd566dd2f60641da26a", + "0xca005d851a4c27070a0706bfb3b95303b7d79008f49a681f052700c1b4275c57", + "0xc365df99690cb05b89c7efc60a7af3bb4e01c22e5f6d51c9631cf737717965c6", + "0x80aac988930e5f1de8d1db964645abb114e145f301db96925bb02cae2f6bbdc2", + "0x7c67c1aa7d047bbb20131ba231a55d0b596394b06b89c574d23b8aee7231b65d", + "0xd2e03510fc847b2890ea18eba3ed90ebdaaed83b55bf9cb5eb37b9fac10517bf", + "0x4047b15bafa722bf1f5ed69691e671a64c54eda20f11d25143ac7e5dd4e9ccad", + "0x746ab5985f0ef9bc18cf60d1216c69d1fd40df2c1bec2262bae7438acdcd42b2", + "0x7fdfec7be4c42f0d16b66818334e49c97d7b06ea6a2befa361a4aba3c511cb7f", + "0xaedb8c3201c978069b5b2b562252cf617e2a0b0cdae62dabef3a04badce1a084", + "0x24dc7b367353aa7ac0c386f4f5768ef1212fa2cb0de8317300ee54b6b8e420fc", + "0x6bf5c551d97c93ed425eefb8f2998b5fb3735f96bd71b1384799a3330347074b", + "0x9d0be98f9e31ad94e6badd0bbaeb85dc69322649b825f8c41dccc8f9a8d0b4a0", + "0x0d1e4ef567e933a591dd361bb254c080f91351b04ded52e022ed296c2c06580f", + "0xb80d22650285ec257007fb6ff1de2c209cc6be02303b3e2bac43c6a57c0d7004", + "0xe2cd2f349288db25e7315e3c8b4257367f082a377ba4384275a506e814940caa", + "0xf56100200b9fee76191568e49bbfa0eba66887f24cbd6cfc0145db6f31da07b9", + "0x13beb775c7a572d1c54631405b45f3e96938af0d5c740c4762d5ebe5f7ceb1b6", + "0xb5b20d1527a14d733fdb3a94f178dcbecfc9f4312e2c3c0dbfe57996e143682b", + "0x5153bf8c2d061de36ebbc21a31c3755f96296936e79584d03ea169d7450700f7", + "0x8d59bf8350b7eb84d5cceecbf735db04270e4d76b66eb9961243af61317a30e3", + "0x90e0af4b3a2806ea91799ee9d973fe9a2a3c7c07574acb7c72665af11a515bc2", + "0x171fc19c2c2e6fa95af7789a01078a34a628f998ed882884e6f514d45642cd6e", + "0x5f73cd024051e26bf239f11b35b757e896dffc144a2cfabb50aee58e2b4c8afd", + "0xdb7401010dd1db0e8af735200f9bfea39f868425afa01bcc404e5136db7cafad", + "0x809dfd2cc6df05328ef813bc5ab86b6ae90bfadf37107dade647e5a49fbda950", + "0xc731c27a9d048ff7ac2f08d1d68d46fa57e61e620e044b929dcd7d076d157eb6", + "0x1d443d7c2a3974ce9d3211aa39efb84d779dde08e132628c77d72cf092ece1ec", + "0x71410564b523d40ccd091e3e19deaad26d74d97963a171cd6e2f739abfacf63e", + "0xab33110fe291f5cc2f469cec31c7d21409345545446ac33c0f49e1e004542f6d", + "0xa775e97c8fb2b70449066c1d33efbc44a10ed6c931b7d4a193e649b853f23794", + "0xf07dd49b627af4aa3253f0403aeef8a3bb452dc2c69a1227325a1c88731ada5b", + "0xfc648f6b75782491734b7d24fde9c7b4bd939d0b338a74a0b36daab26ca3555e", + "0xaa88a296c0cd44bbaaf8148c005f180d7a2757b4ddcc623a43a88a43deeee936", + "0x1ee2fb788d4dc943f726b8455957bc175ebac3e4b8427177a053d7f299a3598b", + "0x13e7c4da8f8982f0ad250a425a24ea8f8b8574abe033e236dfbf596fe2efac0e", + "0x8acdcc6356fc748e03c06ff4878d0fa96f18e4f03272ce01f02ad29d8211f611", + "0x230d99e6323448f1ebcf7e6792e2978a5d74ef654234be97ebaec5c56a684457", + "0xad35598f4bcf7bfa1852a9265a59d0f9914b3b6289a7aa3015e3d808d504aca6", + "0x4d9956ec8c5887e8cce4bab5b6819cc13a8703cb527f14a5e9db99cbfd83b665", + "0x99a277612b78515fd153c88e0060c274b1ab4a60202b575a9b9892420ae657a3", + "0x656acdfdfdf9b76d499176bc3a259aa9722b31c7f3f4ae5c2da8bc3833c12d66", + "0xd06448c98e529a17170aae72cf359a1e2808ff529ca9f37df3360b92790f925e", + "0x9afd24c01afe4856a47db81a6818899d93a5096853b395a24cb9edd693bd24e0", + "0x03b7a5d8eb6ae24ebe158e411df31514229a2f302108984597317196f6226ccf", + "0x6f97a76567724eecec6c55e811085e7a83cd5f6551924f6f8de590b16cd255cc", + "0x5fa0d567d50e49df1d24f243a2ff97feecaacacb6a5d7e34620a9f7e18cb3f84", + "0x727b25a1d44fa0eaea544ac593fd8bbde356a71a48162160feace5596251f1e9", + "0x3d8340ffdc8e124785dcbf85d3b12a65808c49d91a77ed7a1ef54e404acb56cf", + "0x9057ce97cd01e362ac771d04ecdc45b10cfd45d0c3a1070e2c0fbbc81fcdcbd4", + "0x016b166a2ff70c7beec4ecf9a408a8fa4c556e0e4382911057a984454fd9ab45", + "0x9c2a27134a95f9f28d9f313ed2ff1dba33d5a080cd2d00626b8b6efab87d6957", + "0x66f3583ab4095411135af58756f72480d888a24aeda166136e18e25f42d56e39", + "0xf2cbde4bb5fef74ce2342be3f2178a7ec92ec819721456687705025708a2ddcb", + "0x5ce0fddd2a1a63fbe9bb28c01ba7d1c120f2eab2eacb0dc6200fea8c170e5d2d", + "0xeee2051e5b3a37bed40c32e05f141ea33283556a20e42cac05e5efded1b046a4", + "0x0a38951aeda2ad0227d5db28597901dd2f15fb8a53027241941f705f84f3462d", + "0xd532a9afec12700d8a6976dd148621347df54aa5f78fd64581835027a88ef6f1", + "0x2e328120b2bdbe717d673b04c9fe2c66d9320f668798b9fa11c257a5719c12ca", + "0x46a2cc57ba5798731135b891a9cb9af4b1f780a168d91a9d745f5eb39bdf72f8", + "0xe350043ef30f36168e8f68da553667cccd2eaaa4a1ef85c187163b9e5ab243a0", + "0x183ba0d0cdc68e3ac433eae5822bf39e1e351bac9faa4ec279fe9d3304bc6dd7", + "0xdbbfda6ab37d6b32188cbfa975f513f6024e84364bd6c59d513357598c92f80f", + "0xa44361a80cd53311a9e2de8e244013ea6a926cbd5854920ae3e162bd25d8ea21", + "0xd832af8bb66b36e85828711a95a77a2b6f4b42a5865bebbca59209e6585e8c9b", + "0xc7f64dc286ba1f7b6ddb15638662314813113bf058c7a9065597aaa4452c752a", + "0x178b152e9820020d4245e8a997deb02767796841a0220ac22544aa1250f84296", + "0xa182cbcf24b7d477c0efb568a8b120ca341c4b82ff0fd0d6d0b856a65dcf1083", + "0xb1f0f8c06b2743e1f8149ce4dc215f5dfb89f8f1da9718f3f312e500ef06aacb", + "0x5027caa12ed653330800d4de4014e24fe9251ddbcafea58e2c27012dac58c371", + "0x35a7801cd57644a1df91656b0566c62265159106b963368cdaf429472913c189", + "0xe80e4e8f12abcd5a79aeca6efc64bafe1893075bbec001338f625642fc047bee", + "0x994fb4f27b15427028aba87c42b08eb0919e12870a05405e0ef8f2458cc2b279", + "0x67cc00cc4591d984d9c2270fbdadb8c0d4ec9b40c6879aad8176f9cc7131e31b", + "0xd73e02e0c536cabfda569a8da06c99a28f3314d2761e7c0271aa5480dfad3246", + "0x8d709e7a8435b1d5507859d0e4406589b03d1b3ca646f9f26ec3004c6b2d973a", + "0xd5b64c36bc4614f9ee9bdfadab6c095ad183a8cdfa87a9e6858c4b12d9067aac", + "0x9d1d6d5c1eedbffcfcfc2af6aa9a6df958897cd81cac630dcb998f41a6b14638", + "0x39a3945ad321e02ffda18c8072e74f0dae525a57c7197dfeb86453ad208ecd98", + "0x878708a865debc222b72daa2611b7cb8368313c05f7f41e7f8fbbf33f602a582", + "0x305cf1edcfb804a104d777371850939c7c028f50d45f2fc4791524e24b6adfa7", + "0x6f5dac19867a9f14de66769c9a8f0813d7ac7fc4590eb3a862cd14ef63381491", + "0xdd7d64e12a1391437a07fa88b6ea7184676cacfc7cc8db544ab1808dfae8c87e", + "0x1e97a0e1992b75202522362a4edd8004a430f99c024a6503105cc55bcc7b843a", + "0xcc78f11a9f6996cf742875333c8274c68800658fd5a7465bdbf9c13cf2cf72f1", + "0x1fcfc22ea13dc96c9f8f3546f3f1b8f458bf7d1d46445c92deadd848bfee0c09", + "0xaa5d6969d152ebbb075a97c4a051195db68d90a75c37c104321131c073fdd843", + "0xf4f6e0c5bd269c7de9d1473c5074b3bd4d42b207742b918ab1481c633d05a38a", + "0xbba5eb96150613be2e45e1ea8ecad915415cb1e091a85b6858a7d1d5a8a005f4", + "0x1cdd61d797c519d6295196df06cffd0c9819ce0d9315c66e5309f14eacab63ce", + "0xb49771b39eefe9791d25ff5a1c94f0dc7d560a90cd9ca9c997625c05c0299783", + "0x5232fc02627cf406984a2f4f2e3813ad6260c1b07da797c2d1b7218d7eb05ad3", + "0xecc5ee2ea8e3956d964bed681f61323f608bbfe52397b5b28ef26f660e3c214b", + "0x49ce943abece2026b0c51a5ad5bd35613c92f480027bc47954eb9bc7cca5bb0b", + "0x27f731e278a0221daf21ab225b0297276db74ab79edc4806aa6a794e730f615e", + "0x55a9b21ac8387ad6aff87660d889be69d8b506a5de230043fe5937d0bfa53771", + "0xdd2ba4ff24bd512d48b553d031130799e46f3812c4b782c9423235ccebd7e655", + "0xffff9cc54e4484d4e70a6b3770d11fb9e13e583522ab4bc376cd6c7498021d65", + "0xb4bfa3c62b210ad81177faf4f6a170dd801713e5f9e3e4bcb7301c81cf60d083", + "0x5c3f5d4c0836dc02a32d7059c9598bfe46a813684f3576ec24aa8215a5de6270", + "0xd6d8e57c79dc17f19eebdce62de1adc0940d881d61c4a1099d51e6cf0b1009b4", + "0x2d3b6873546a1e6d026249e0cc45ad822fbdbb2ebcbb13832b7862ba9bc09982", + "0xd0d2b37a28cd798dd9d1a1e0892e86d90ebd54e6be912ff122402f6d923becb1", + "0x0345ca4471a47d8d12421bb39b0b0f7e8475d04181abfaa9050b95cde9aa4a8e", + "0x78a603e3f62c47845e657de355bc40d877612062b2f42b63c50645f3c12c4b15", + "0x1e973f2f79722999a5caa72528d1920917d7e7eb6839976ca19165c3a8bd26fb", + "0x3f5242f783e2336e5cf0cfee1427fbb15a833930b22187b3659367563a529039", + "0x181667e480e8017caf91681eb04cfaf0560254028a903b620485cb9771cc26d1", + "0xeaf92b93fcc996d886bca5fec1f6a7dcdb58cd7acd8033637a8fd9d4bda169b0", + "0xe5164f795fac046d3106dd707c4628d731bb73a3fed8d15ecf915de5b2f0a48a", + "0x556f9c42a01e441bf0e74c4a89323bdea6ce402dbbc1f500d5b00146be5439fa", + "0xe59b25cc5415f5b84b41ab7085b854f64f6d839085e85e6b2a30c41e6f689494", + "0x9e2c804effa40cd7a92ac8901798c03a5e66f47496cd89fd3b8314e7973f3d06", + "0xad4eec2bac36a3ea837c95bef538000dbc75374a4c4f8ffa7d2705ba300aebd9", + "0x12212ae8872a77a5d57a4f5fd91797c2fbf64ac2fa5af09aa55742b3950d8e88", + "0xd987ccc0c413e4390b5b5b69370f54b5d35f79c9cf4c1c534598a7e383399ebb", + "0x8d30d2dc8afe7f50e160fb4d3cfc5c54a962f31ae5b4f65c2362ceb7b8cc4733", + "0x617cb9b3cab2b8c43b11bc3412cab6f889197ba608c068dba2eaf1fa051b0f26", + "0x75bf3d53043c8375f1e51194be0942f3ec73ffbd61926d33910fdd00dddf5b87", + "0x72bd67478fa033abe7905125b728485f401aaa31694bf9ae7eb1184adff2db12", + "0x23d7a73706b970688eac1e8affa5524f0f563228aa7601fe7d790f844c186bbf", + "0xea350aacae57df34a533519b3448435879faf637e3f4a7cd3bbc891489d31caa", + "0xbff826ed9cc685de15275d59a299f5272cfab31750d39141e5ceb8f5d3645741", + "0x020ecefd03dde3543cea261d265a265898f6804230e95f044360d4aedfe031a6", + "0x71645432ccbc0826c83dfdf044ad7e3b2df4bc979e68513ca382ff93e24d1d77", + "0xfcd3731fa2259ace8cce5eb1a3fd74c723f997cfd8d4192105c41f2874d22d55", + "0xf13985444c5a6873217da182e9784fa87576829b9e841dc209a2592f0f5522ad", + "0x0e065da66efc90bd88217d6e9a5721280c4037de294d9e6ee8c1207b664217af", + "0xc6f893863ef60cf57991974de8edfa93c28c55c3af96f94cef9360be9a3a05b8", + "0xae2bfbe04bfe046166f0d6871d689a809fe443c9a6cdebdd5b2e32a36d7fa67c", + "0x733fbd82f21056a2e402ab050e32cc83aae96a67dfd3fa9c50732a3fd576e7d5", + "0xf4e29375a5f989dfebdbc9101d411f062c1090a9869058f6b87fe19b3a259489", + "0x95f902f47c21700bb030841890c47d759dbeca59983f1f2d5353674a4f45088f", + "0xb02a6050eaa94106ee9d4577b10f6adbfe7705a3ef9c3019412c4f60ee8d0e1a", + "0x20d64b55c9e4c2eb51e5af544ed068ce9d323c194c5b18bebea4bb0182344ea2", + "0xb6e40dd521e524b30139719580bd7441a35a1bc304c338c426b706673e179320", + "0xc54752027833b57236b5f043fed133b2461f199b75d52da8578c6e98dc2fc5da", + "0xfd3f9c65d1b05ccbf980a35cd433da01443ff8fc7be680892ea3185914d86727", + "0x7175b164364820f36d10f9e079707a40b21004d0687f90bab1b132c9c34ee2a9", + "0xe4c4df954bf0d3d0eff516775d60cfd29d0e780da4f07589ece3f11ba8925b2f", + "0xa927f17776d750c0b6996f1d8d0db22547fffdb006c0cff367d0a0edc41ddb01", + "0x27050f4807ec856a205518e9d13821b2cda0231cd8959f47ee34832181eaf8fa", + "0x4ebe3a5760406a59770cce09a0a44225dd5a2fade1d877b219558029e30ac4e1", + "0x290c328e60c366b0d18e55c5578b9b3bd83bf1b0600faf0822a1f6f1ae85edb5", + "0xd7bab11bbd83da583e3fcceb0f84bb551cda363c33c67f93e02800176c29bcee", + "0x3cdcfb49d11cfa3ba1809271869fdd9071017e5498f8acb01c595dedcd3ff109", + "0xc75582264ee66717ee40ff52a6689d03121ce0f1abb2c354cff6d92e532a81e5", + "0xe7004c5a4d09fc89e8737a5535221b552e67c3a0fb5851021e9f9a0de4672ab1", + "0xd6b8f94deccddb6d4f8b066c0302faa4b562f7a04e3a092b7d87b3a08485d933", + "0x2e726cd7212cdfd26cf0d928932b05ac8258003254e6558c87eca8a82cf6bb02", + "0x6c8786a64069e3f422dc5000e6a614adf14215ebce0aa8ab8e22c5f239a3714d", + "0x2f736729adc62370324b478cacfc6256fa0cb9119219a2a000d2c34c3293f6c8", + "0x8afc8743b959ab340dc86c6ddd9ca1726cf4bbd13054616b3d1d64bf656e0827", + "0x661832363afb109885055ee90663ea7514059a2c6d69144f8892138e89ed037f", + "0x2279ecda37192639297242d97723c6b635456cb2c98266493552e618067fc5ea", + "0xec7b510975a50daf40effbc94c977f508c84a9712cdb28d60c80b30adb3fd24c", + "0xcaea827e123419fe48b7f72dd67eb8c3965a4504f13e4ad7443bc8e87bd7c832", + "0xff012579799171dada6dc819977c222836b9cbb4f521142e74e9ef0762f73131", + "0x70ec4b383c92a8bb37f733825d60e76e75469a71b1d5361a84e37b38e3b8a34b", + "0x4dc584e8bce8efed0a55021d334b87dc5973de45effbc43e84acbfc03592532a", + "0x0fdabd0093aaf388fdc53d49570d3304c8c583415eadabc98635dc472f366b90", + "0x1b29d10d1443bb37e47668c9b6a124a1964a3f12075ab9be1620891579d8db31", + "0x3d350137648ba1e593182918a4ddb6b5983001356d6e406bcb88753449ea55f6", + "0xfc3911c59399e8c329db2dadcb5f7a58855f19401f513b9f6bdeca01e73edbcd", + "0x89a4277d6bf214c50fc0a7219ae59bdb719b71094d6a02c4ef5e07b50501e74e", + "0x7158b6d3debe3b6ef5dc0d40616691c804581d5614714f2284e63df42b5ea578", + "0x46d73cd458ee5a26724a68b2f8186930104aa9674fe4d94ce75215c61d0ede81", + "0x100c71337ae5379ca5d43f2177f2f6e6e57c628bbae4bfb70abdd1f23279787c", + "0x3c9e2e0161341c82207498608091846726ee4dba0fe1fe69c07a92a0ff7675c2", + "0x5c4885fe3f85ee61e50378943a48732843d47184619f40954daf24a165f2ba9b", + "0xce300dbdbc2c8eb91c75f7e0e8e91dc03ba8894478a9e65529241ff462abf713", + "0xebcc160dcb2f38f4607cfd3ae74a15286eb0aae05c8fc4f87abb751ca88a699a", + "0x91aa4f2af47471ac51160d0d1c81137c7c6a22ada295356668427a45d21c6a07", + "0xf30c1a0842faa5df4ef896e192d5902445ed7b01cfc83dc287492b1896126310", + "0x0e069a0772dd736ea68fbbef47302ed6e802868d0847518ea5ea2eb34a79705a", + "0xb49c6e624b584d49ba226f31611a0c4f3ff0033a746b370890533b209da2ac3f", + "0xf2a4cb9c4877795e0ee2964e5aec8828941bef07777ff4db07423b4a2eae5a31", + "0x2e9cee7389233b6b4937f5997be0a6f37a8c347c58886b75e3013060c10c4a82", + "0xd2318412b7dd0eb4f6b3da69c2c3cb7d8a6b7028ea8f0a39bc3bd513ec7c6d3c", + "0x74c00a1f66bbe517e7166993e18a1564d43877110c54be28053321721e619e86", + "0x37de40b83aff733b4792e27dd6211687ad891f8d95e397e3a3e07d481a5c44a7", + "0xb607adc39f83da6b0a7da38893a95cf33582d836c967496ea7636e61c6cb7191", + "0x518708d6d86b3a15dfd50d0003c340998be42ff59d533703be30763aa080c547", + "0xc9cc7282488774cbdb9d6511ebae8f6eb9947cc5360585d75197532e37e9c7ae", + "0x9c73d1c1a937f888d29da748772785d1a8269282003e7cbab2af4140039312ed", + "0xadc5c8b549e56222f6634a308348eda613afa171b5991ede1b127f241d25c570", + "0x0bcab0832ec43772bb1aa95f8ba9eac83b4b47bfa776876276b47722aaf5c3c5", + "0x8b37849d079adedcba7ed2be598a6d84464ccdd807ccdef2d711e5ef7bdc003b", + "0x8e2e677e5f20a78caa0ab3726528209bca791551d0d33346144c5e7b1c8c6bf7", + "0xda0666298d3184c66e54af103e988fa0c9a80a12e32921e5bea0233a8638e23a", + "0x1ffb4873b85e71b067e3ac99bb26e7f5ee5e036bba4835989f1477ac8b03466f", + "0xfc89d4900e3e7a475b8923bad15bf68f4cb967cb50fae648c2efb3d90cde84f9", + "0xb62eb8ea8e54ed6358a752943acd7a2a1fef9553533b56ca17f3cfc0ba719ec8", + "0x795e2eecc65f357e0b7a9c97d2c028b7503524809adc6e783f05eb0423362980", + "0xdb06a90870bce8df7a88118d485eb691a0043c695ceb64af565ed62d37770c98", + "0xa28bb65d12c4875edd64e643da53c1b65a154d9e40a4cd37c6f770940235740f", + "0x136902f6e7104043a9d828a027df5f23c61a7d085e49907042c55d99d72c16d7", + "0x058424a9554f7a52bb6516ee51653aee32ea09d96247fce31dd8b31af52a07aa", + "0xcce2dbdca699f427d620870ae23b96b38da3414c57e71990ba8af36a58e6b936", + "0xd1feadaa1927f77eae9bb7260fe84ada5d524bdf1239a6e688080da9f7b71af0", + "0xebe906165c9808c0b368392dd2cd950b55e1f95394d28255d7cb9848bd4f3839", + "0x639198f58b4712fde6064dc49f73b3db52726e76ab9bdc00f6ef9a0487fd6d5b", + "0xe9e2f1d0afa3d3b0883fc7269618ca1a50ea5aa828ea07f92fae48b2b2fc54fd", + "0x811ee53510a20a012a10ed6a02407dcaf98099823a35e54849b381d4a6e5b5e8", + "0x221c81c3c384717eabd545830b62427d11fc62bd1df260921a8e121456d719e0", + "0xdc62196c03031dfd983ad4a211a514e25c4c97c9cd9ca62ac18b44cc0da4e0ea", + "0x3f60edc7443b2ae2a4ede7f52c2c4de9d58f90d6be2f0fb25772b51c352aee17", + "0xb5cdbf0eda83613a613ea26b14401361a2de1be89412367f39d4ab01449a87f5", + "0x10d0156c9a828e9b8d61e615079beebbaa525a07f32d792e5a67d4f03846bf4c", + "0x4be38f1cac74f26e2e5bed0d2b416482a74aa425564ccfd3e0ef10b5f57a7d59", + "0x814aa2e560adae5e7192ada4ee11567bfebf3ff8a2a93aa8dd432df30c77f44f", + "0x187c004ce45887043188c222842da10e062a9be8a3db326d3ebdf860209e723e", + "0x60036829b2cce1932f2d4198b5207d70a2e0634c929a63df122ea38f620f89eb", + "0xc323959ae94b9d4e5246a35ccb887b8fcdd2b5de43ab581e53fd3090b4dc88b8", + "0x37a114fb8c7c973708d5dba9c37df501db5e0d074c757954e52ef716299239ec", + "0x8cb29fadf7071a43d8208036d986abe64a1974786c4ab573f4dcca88f8db83a9", + "0xfc4485a7b2ddece8cefbc0f45c949920b9412a47b3e21efa9aa381e0cba0ee72", + "0xfdd52d72e5f42cc1cf1bdcbdc97b15f623b78d78425c338fe588e890be995597", + "0x92e26312a466aff28597bf3444b0bd298b12872f099885ab10949c0d263446a5", + "0x420d6f0581dc5c914b0da7339fb69bb5c9094ea8715c778e9dccd922317c36da", + "0xdd8641edece5a0ab2b024b4ceefdc3f524cb0cc9549c200528962eb0fa341310", + "0x740c0963ec5c29ca9fa48445ddc604bec138df538edb18844182556924d09fa6", + "0xe66653af6368613260ba344cc051d6086a2e08331e8bc5ddc2d25ab06ead9eec", + "0x86e1aa0ab7a4e47828216a25073d6c355e2f4ac4773cf1d2d859f62fd8fb8a24", + "0x3be2cdbd947172907f5605beb7863e2edc5f176e0960904550358ed6c2670704", + "0xc7d4869adae5a7593dbea4f802ab068044dab40fe0063d5b7c7727a3bf288525", + "0x620611d92e256bf7cc471c8883801be5d64fc8b77c1f39682bf840031e0dcd3e", + "0xd89f0456b5a3d041d5309ef623e259003383277f7f509096ae5dd02d312543d6", + "0x7d91689ff4e212d6f2c7d62cdef0593686ba04aaf455c576535d097120f1353d", + "0x568a6763698a23b4b2289b851b46907ec5777284741d3a7304d7df297fa4129e", + "0xb9dec1f84c9b71d5939f40982fe5c88d15f868e265d4fd9fa0560c2b64191998", + "0x6620f3e9e7fae45ca0178116a9e478f95bc0a69fab3a8341dca673453741bb6d", + "0xbb418a7f8415e5378cca2ee7056c6c78641b085e30bb9f9da033ea193bbfc77f", + "0x002fe11d761d3135edbb3dcb50533a1b143593145f7b4cd914d84494b1df652e", + "0x5f76ba2c72c3e4d93d98668455def3d335b03f517648c6cfde1d0a37178a0ec1", + "0xda49060ebc8b7963a9ff4095c9777d902075967d75c47ee3e9f86f03031b0a3e", + "0xde325b667d85828b7416d437a91c19a14b0217323650dd5880a8a40ea85f451c", + "0x9c14e5953fe56c60599dea8284b0948603334fc7aa795cae80308eff4cbd67da", + "0xa9e82869a1be8247c665d86f52900784005853c5249393fadabe37d08752ac9b", + "0x3feff38ddc1917cd5cace417b4e86762126997a5420ac2cb32abafa769f91ac5", + "0x01144f9a6fa0b4396560cdee8bc6e873bdf3ec5c3ab3f7409e59884e6d2dbd05", + "0x96778169c9f2b805e67cf18324c292c8b67c57db72aeebfe5369d91aca0983b3", + "0x373f81dd55fb3bdc9de4c42028a5bdc970cfda825723b6f0f7804b50a1754bda", + "0x3af64226f4eb3ea6bd6d0dae027bce6b361b30c50b4a65a4e1661577268953d0", + "0x35b96aa54e1c50049a0c91281c75d3d06c4b609a2d3d92cb22d1328369adcdc6", + "0xb02c9650fc610b3f5a0b547e8bc505b4a840e1dea123b4cb2c8af6232ae1359f", + "0xae53d65eebce31c14b787f1df5b021334312e8d5b8302b84f6a56b6608e25d97", + "0x4901a419abac1937602d98c21e937a03db8a48d088e56895788da845829225cd", + "0xf5636fb8f3a381476267e6713f10763cef51071b6d981a8f661a24ff6d368e71", + "0x059e8319866374d8eb8372e71998cc9277c5f5e5968c126a81d87d0d7a6fe8fa", + "0xd5fbcd3d7a4387000e014e67ef61d4a7ce09a70374ad4fcdb6ca0b6b50b13ea5", + "0xc37ef88d74d307090a0dd49b69d5705c8e65ad606548b0880eef33b8346c2af8", + "0xa316c8a8ac949af5957fc905ba39c247a477224cd9c4cbabccd0671dbc1e17e7", + "0xdb313f10f0dfb56b8684e6ded5e89c4d5a3cd958a99b1773680db8682645b9f4", + "0x0c81ab956b95426f5c327c6ac6db2fbbaa8fd0f41cc9f58971d5089a0f3c52c5", + "0xb09827a7c1bfba1fb7e5c7acee1da5d8fda1c10da19046151f55ef7fefb784b9", + "0x4332b07750799b436dc5b8521feb0927d7e59b25832ac83876da4eb9df333aec", + "0xdf6895278968d74b85e4bb2c53d70772a6176322200f864b004ed914bcc3cf2c", + "0x9a4a6427c038efbb77e255b8e74df6f897590f734328e5be0ab6b16a6ca58e46", + "0xcf683bc5fd50f6c444a84a5524b3e6ba381fe62c39828650f8c70d9b5b6e8d47", + "0x3282bab4eea18b9a8147d6dbf26073882e7f859b498b2d17608cc96548f5ba7f", + "0x04684c3e47911134ccfa142c85d283f26ed315b5818a523a79e9be31c17b4dcc", + "0xa8efff633914b0ed52a2eb3b75d6efbe1efb0f640136807db13d8809289e40a4", + "0x4502554b0b48e3230e24efafc4f8eef7ff50347cea1a27a15ba46187ed0fb2a9", + "0xe0d6508774e4d8772bfed391286d5f13b986518dfe49d6cc9fd55c415e2382f8", + "0xeefb070835b6cacf8237566bfbc77b177ad0e3e6293587d76904f5cf56cfe62d", + "0x06162414a2b278bf48122286cfd04e83ab148c91ff45d5b90bb3a55d374debb1", + "0xf87fcab4bf30f560c5359ee594e263859f1160f3ddd4186918093275e271d520", + "0xf3ea8332782f88dfa789e42eebba3ec9672589d090c557e36e05c7eda9c7372c", + "0xf05f9c5ec95884e6da30f95d56ad58a025dd696215ff57683d6946ebce25d497", + "0x6738c17f58a1e7d41a634984b98a5efa47e910640da0fcd80d2ad0603c0ec9f9", + "0x8c2029217e14a9bd4dd88eefbe6ddf886d30c56df42035464a5acfe567406dcd", + "0x5d0434b926b112a81e7276b96d1dcff0391e782dd68003173a5d11701a8fc190", + "0x4d9a5c9209364544eddbc545822bea83af527fa1f7b24dbedbdda61b69e18250", + "0x3e5800a59c58a56bd2cb7659db0705f45b6b8e827cf06eb0fe98586abfc83368", + "0xbc3f4d3a46d5fe6df5ded66fc47b5b73baa878c9b8fc3798e87a17995614c515", + "0xf715cbf347c5308a4d50c99eeb4882c6244a03ca77349d126337ed3861e454a0", + "0xe831c5b327832f9440cfc795e7f703cae82648704d05f7bd41b2acd4e07b2c1f", + "0x46c6926581d617ac8384583726ec7088f1e0df60bb075a1b43ab51fbdaa108b7", + "0x236eb9d731c98bc8407bcb9954e3297c51ed6a116f90f6d1ccdd8f4c89bf5dd0", + "0x6ea6994bc6b93be59e0e8f48e32f2afbbcc653d4c1b845625b8a5e280db809bd", + "0x3af35cfbcdd184c7579a273a73361271ae9e1a97ef89fb1f0e6dfe89a776c237", + "0x7553093da896b06cda700a41c0233274a089e633dcb5fd8d81ff0393491db2c6", + "0xc555ae61e8f86c775768be4a07045042a8d5898ca6dac14687327b89303d9ad2", + "0x4fc388b57b8d35c704e8c03be70f1c9ba03e638ffac704791c0aeec6d3dd8e2c", + "0xfd0ec61b6a525012d03781c254697a98ac0d4b1e55f46e3aa1ab819e7a90436f", + "0x1d7e68aa18a225ddede2856f421613f88872e89a7024919590930c43326a5bf4", + "0x37865c43f2db20bbfd51a1440e7cf95923c6b980ac6b7e3173d3f867d60d9b32", + "0x44de3419b85216c1d2ab63cf3a002c28835dd084c2e3ee0eb4ba92dc49d5a1b5", + "0xd5fd4d94d504e021eb7865b8aff7ed53c6afa96970fbacd678c2b78fd8c5badf", + "0x479043958f7b84ee3c20a9503fb10b3ecbb895b2b4eb4f69b3ea0b5243f881c8", + "0x683669e82fd55e4162c00a97b2af897395e3a1d3dc51c7f12f4e01e4e3cc3ef6", + "0x9dfc309c9a3382fad72a40825cf621e1dfe095aa77e3b00bd5a0f0c5445d6a34", + "0x63def4af1a927f696cf1abaeba3c172de583cdff6278d8db46e23ac8063ac042", + "0x682e98b0482b1282c896bc2920ce48328246d8d3301b5059988325950f19e0ae", + "0x929abbe6a8ebc0ac32f5ec6a8c1f728d733284e81c02477f606163521d86f642", + "0xd3deb41a543048541beb8b96a1f5b029da6ddb513327d025a5176e95dd4ab1ed", + "0x886432bd185ec9c30a459182ac4467aa9c0f590de8825712c8fe3d9cf7bef9eb", + "0xcc9f64743d0672261a614e9e5c09e9f8416d754f9c536e456a08ef885569a39d", + "0x55bd4b7d2d2ffe8ffd7054d1c8f912119ae793a23dd2d9a10f0ba94ee4b8ac9c", + "0x00a86e2d5dcaec027c9c49853eba467e910f39b4cae50bee85c40b48d34313c6", + "0x89ebc269c4a123f9622e89fd221d22776def13c4a9637a7f69176504ed48ac2e", + "0x808d7a4793aa0afd91d35a6a46eb4f3ed522c77716fbd27765ad2104d2638d75", + "0x5766e58d79181a27639e4e2b1c141de7825e6abe3996d3d06a518bc87044abb0", + "0x53e304b2ae212fac8b059d283f8f97553cb16fb3332e8833305a32db18abb5fa" + ] + }, "nodes": [ "enode://6332792c4a00e3e4ee0926ed89e0d27ef985424d97b6a45bf0f23e51f0dcb5e66b875777506458aea7af6f9e4ffb69f43f3778ee73c81ed9d34c51c4b16b0b0f@52.232.243.152:30303", "enode://94c15d1b9e2fe7ce56e458b9a3b672ef11894ddedd0c6f247e0f1d3487f52b66208fb4aeb8179fce6e3a749ea93ed147c37976d67af557508d199d9594c35f09@192.81.208.223:30303", diff --git a/ethcore/res/ethereum/social.json b/ethcore/res/ethereum/social.json new file mode 100644 index 0000000000000000000000000000000000000000..52b442d0886b844f9029297bd24e175b138d8ae1 --- /dev/null +++ b/ethcore/res/ethereum/social.json @@ -0,0 +1,8857 @@ +{ + "name": "Ethereum Social", + "dataDir": "social", + "engine": { + "Ethash": { + "params": { + "minimumDifficulty": "0x020000", + "difficultyBoundDivisor": "0x0800", + "durationLimit": "0x0d", + "blockReward": "0x2B5E3AF16B1880000", + "homesteadTransition": "0x0", + "bombDefuseTransition": "0x0", + "ecip1017EraRounds": 5000000 + } + } + }, + "params": { + "gasLimitBoundDivisor": "0x0400", + "registrar": "0x0000000000000000000000000000000000000000", + "accountStartNonce": "0x00", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "networkID": "0x1C", + "chainID": "0x1C", + "eip150Transition": "0x0", + "eip160Transition": "0x0", + "eip161abcTransition": "0x7fffffffffffffff", + "eip161dTransition": "0x7fffffffffffffff", + "eip155Transition": "0x0", + "eip98Transition": "0x7fffffffffffff", + "eip86Transition": "0x7fffffffffffff" + }, + "genesis": { + "seal": { + "ethereum": { + "nonce": "0x0000000000000042", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + }, + "difficulty": "0x0400000000", + "author": "0x0000000000000000000000000000000000000000", + "timestamp": "0x00", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x3230313820457468657265756d20536f6369616c2050726f6a656374", + "gasLimit": "0x1388" + }, + "nodes": [ + "enode://54d0824a268747046b6cabc7ee3afda48edba319f0d175e9e505aa9d425a1872b8b6f9ebf8f3b0a10dc7611a4c44ddec0fc691e5a5cde23e06fc4e4b3ff9dbef@13.125.185.147:30303", + "enode://7e150d47637177f675e20d663fc2500987f2149332caf23da522d92363be8a7880ef9150a6183e9031288a441e0457239474967a111eafce17e19a4288076ea9@18.219.40.235:30303", + "enode://6244c9d9cd288015d7ff165e90f3bb5649e34467e095a47c6d3c56e8fb8c849b3b4db683ff3c7ae8a654bbdc07ef12ee2fd7d72831ac213723281c1b0cc90599@13.250.220.98:30303", + "enode://e39f162b9f4b6ed6f098550f7867c2fb068fc66f362b3db0f45124c43ea18508f5ceef4e0e4de53d301e14a6f1683226aeb931d7401b4e83b5a583153ffdd7fd@52.57.98.157:30303", + "enode://54b4a117d66dc3aa93358dec1b31d4f38e72e4381b3e28a65ac6f1aaac3b304ebbe41d32cc864fa69a9a6815c34cf9b8965690dc174a5f72af14547b601b7924@222.239.255.71:30303", + "enode://851f14c5cc86cbc0a81acfcbe5dd99ad5c823435357219df736932c5f89ad4318f6973a553857a32d97a71793f5a35c062d46320be282aa0a80b06b9c6b624e4@13.125.232.71:30303" + ], + "accounts": { + "0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, + "0000000000000000000000000000000000000002": { "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, + "0000000000000000000000000000000000000003": { "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, + "0000000000000000000000000000000000000004": { "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, + "ceed1c8254abaf069669fc6045e90482543d1f2e": { + "balance": "38000000000000000000000000" + }, + "6f22d7f8c38e0135e36be87e77fc8d4ae4b3d965": { + "balance": "38000000000000000000000000" + }, + "d016e982b7886302428d7c741392c658337513d2": { + "balance": "38000000000000000000000000" + }, + "defa96db5c8a41772bc56f68f95e307ff71a2c60": { + "balance": "38000000000000000000000000" + }, + "951ffd4253ffcf31b2895dd3f7f2a8a9bb2933e5": { + "balance": "38000000000000000000000000" + }, + "b7071dba21cfbe1b70abd7ddfd2f0f83d5d19a61": { + "balance": "38000000000000000000000000" + }, + "6ca420cd8d5407c61a9b14adcb38bee0f26e2848": { + "balance": "38000000000000000000000000" + }, + "b50d185b6cd04499a38afc0fcfcb59eaa74d0956": { + "balance": "38000000000000000000000000" + }, + "7ba8c49a444c117f2d2a50650b3f700d4ee659fe": { + "balance": "38000000000000000000000000" + }, + "83f9959ecc532dce071fcd0c62dc23cd571b689b": { + "balance": "38000000000000000000000000" + }, + "001327afce7ebb623a7d0f17b2ffc358fb863b5a": { + "balance": "9844198127334000000000" + }, + "0015ac7f8bb2a2c7d954fc2dbd4e20c0db5942a5": { + "balance": "1000000000000000000000000" + }, + "0021e69c041be2d28744b69361105ff51295da59": { + "balance": "1379639894000000000" + }, + "002cfd27bbb8164681b8762e71b2891beb127fdd": { + "balance": "25105286685000000000" + }, + "0033cf217bc765ccfc338869451588ce448fde65": { + "balance": "23425485280069000000000" + }, + "0048f1d1735979cd8ac9c0886088336b2d4a43a6": { + "balance": "10000000000000000000" + }, + "006a79cc154917cf204d8097728f290e29716d43": { + "balance": "20000000000000000000000" + }, + "007c8db36e4f649b14516dd78202670b671ba753": { + "balance": "1000000000000000000" + }, + "007d131b58388f251075a3c61020ce301106c5cf": { + "balance": "381079535880476000000000" + }, + "008e8fbffc2fdbefaa7e3be4f4a9160db826d05f": { + "balance": "10000000000000000000000" + }, + "0126d86b9814b0e78c4e01a3916bee6a7778145b": { + "balance": "10000000000000000000000" + }, + "012cb961297c837630251a173b7861e77724856d": { + "balance": "494562376059000000000" + }, + "0145886dfab5ef4f2def50a56b4a074cf5b18acf": { + "balance": "680585022951000000000" + }, + "0167918bab62aa2118cfa4d3eb80da0e71c71d8b": { + "balance": "122395119468000000000" + }, + "0182f7286ae9d4d6bc514d5175d14685d520bde7": { + "balance": "10000000000000000000000" + }, + "0184e9c8fe99d85100fe28a0e8877b14768b372a": { + "balance": "193295614762000000000" + }, + "019eff7dce7f31c2d4f7318da71d212cbf89d36e": { + "balance": "64004138198000000000" + }, + "01f3351ea66c352346244dbb79189066bed62fc5": { + "balance": "937056266523000000000" + }, + "0202ddd7f4f32bc575f7df24612f8aa9f9a7ae42": { + "balance": "106905151080000000000" + }, + "022b8c65e959cab71f56688b7073257b58bbef4a": { + "balance": "9682681149566000000000" + }, + "0245ff6382eb93ab1e8ed2e3c0bce7a1b9a9713d": { + "balance": "289000000000000000000" + }, + "025e117a69ca9244ed430732c11e550e3ee67577": { + "balance": "1861905310567000000000" + }, + "026e7457eeaeaec7898fdee1ad39be89e92733b3": { + "balance": "7529030978000000000" + }, + "028d2def8c54fcc77bf0191b183c0bc4570ec1c5": { + "balance": "9056720934000000000" + }, + "0291a087605b516e465134797b5459436d320e6a": { + "balance": "481100929805000000000" + }, + "02a4b18b3e13ec79307e712fba1867cbf7fb6155": { + "balance": "9000000000000000000" + }, + "02a812d4cebcac1a92ae470ade92fde7bead127d": { + "balance": "66793603497000000000" + }, + "02f718c94b2c8e8d62752f8632443504c1e4b6e2": { + "balance": "1000000000000000000000000" + }, + "030dab52b47b37505b72e5ca0985f65ead590816": { + "balance": "1376851176362000000000" + }, + "031c59846f75de1aefb0e8a95e0fb822fd06555b": { + "balance": "140978338628000000000" + }, + "031ee87c672d83ec73059c86a312a9e972142054": { + "balance": "96406273341000000000" + }, + "033182e860564cf695cb403c8c0f078053368d7d": { + "balance": "1504349255824000000000" + }, + "03788dc6528fa33a90e90e03295ae4b792d56644": { + "balance": "24356250426000000000" + }, + "037aae119047028157c5b3bc9d3d202b02cbce42": { + "balance": "105627739575000000000" + }, + "037d45b323cbc5cbac999c5001169646e690b94c": { + "balance": "2000000000000000000" + }, + "0390ba35c454a24519d845405a7e24df71250748": { + "balance": "1872501898509000000000" + }, + "03ab8c1e7f904db62437b16d28aeb539b2dee55e": { + "balance": "55000000000000000000000000" + }, + "03af4c69728a888fa26b1aefa439005989771fdf": { + "balance": "27704588237757000000000" + }, + "03b33fb19d165e5e33bcfbbfc009f418d71f30fb": { + "balance": "1999139000000000000" + }, + "03b34c79f8167a4e8be0f6133254d2b50cbd878d": { + "balance": "111065050160000000000" + }, + "03d7e8638b74ae44a2770287285489a95fa1ea11": { + "balance": "20000000000000000000000" + }, + "03d870bf719f03250c0dc5156a36751b3aa21f18": { + "balance": "981119649953000000000" + }, + "03de52c12b05fb8bcba3a1cfe02a0ad1bc9761d3": { + "balance": "362007990932000000000" + }, + "03e516db27b1abe008ffc57ced48f72f872d8b08": { + "balance": "3389116345657000000000" + }, + "03ef5ff863ee5f1167f38cdf316e4d52a242b750": { + "balance": "12395856876000000000" + }, + "03f27766760f2bd1cae1cb85ddf43ab59c871e47": { + "balance": "49000000000000000000" + }, + "03f4e1a8fb4eedc776f4835fcc85d0f236612f9c": { + "balance": "27210590272692000000000" + }, + "0404936ee04cc79cbb3aedfa33a53f94940f772c": { + "balance": "29919204637416000000000" + }, + "040501ffde9649be794b7d41643273ed6285ab39": { + "balance": "686000000000000000000" + }, + "040e449de680f69614120f0a2e894cde36e4adf1": { + "balance": "81993180085000000000" + }, + "041531e906dbdc70d89f5e255151d9865a059308": { + "balance": "2163418749315000000000" + }, + "041ad9f6bf970541e4ea8a14dde1e789d0fe4367": { + "balance": "44999979000000000000" + }, + "0446420c07cf73d2b3741b945c1cc8444b4ba6b6": { + "balance": "138749371512564000000000" + }, + "044c1a540b8ab286c218c2fa9d5bfbc2761e7626": { + "balance": "1" + }, + "04526b2c62911e78a939816aa4575fe30baa06c7": { + "balance": "2983093150081000000000" + }, + "04adf59f8a0ad3820a7972c6243202b3b0617fcf": { + "balance": "50000000000000000000" + }, + "04bbb42882a475eed58aae47fe530ed19c1cedaa": { + "balance": "278038763863000000000" + }, + "04cff7a7c2b9b0bf31c5ad4a5de8b0eade70aafc": { + "balance": "515406205244000000000" + }, + "04dcd325dc1fd37ff3c87da3b21c47ddfcc37cc2": { + "balance": "5831887315000000000" + }, + "04ec8d0b5157370f5a2671a2aa68ae486b7a7842": { + "balance": "10000000000000000000000" + }, + "04f50f2a6e89ee497a64c11baa90759a10a1247a": { + "balance": "25443071621949000000000" + }, + "0513a769ebef58ad3a4fd7011ddbe19799ff5600": { + "balance": "64369494797000000000" + }, + "0514c1151f356070ace281435f25c86b58280715": { + "balance": "38967380881301000000000" + }, + "052fda414fe1279c6276a237b07e1b4148a8cc77": { + "balance": "10000000000000000000000" + }, + "05431089cc62a987d0e99847e10a006233146f6d": { + "balance": "268977485219000000000" + }, + "054ed2f55028257212b996f9a3d34758d1d4ffd1": { + "balance": "100000000000000000000000" + }, + "05a68cc758560addde302baf814f2fdbe0ef2c2b": { + "balance": "10000000000000000000" + }, + "05c669d9ded79fe9e4e3718052bc7ca18a3205ab": { + "balance": "7347158140000000000" + }, + "05d77ce5c87477b05e90e829dafd8eb3a8c87823": { + "balance": "21191998933000000000" + }, + "05ef2894a2a1c6eb6c0a768d04ef5b573f357712": { + "balance": "20000000000000000000000" + }, + "0601011f80279190b96f641205c6a524a8ad5a28": { + "balance": "12025716447696000000000" + }, + "061a8acdb1a340ba7c550814831e27262708fc98": { + "balance": "234029764576000000000" + }, + "06219483e217c9ad479f76a95426f689ef4d5951": { + "balance": "1509408723551000000000" + }, + "062d0db8a650f2241f8a4295326a2570a7c771bb": { + "balance": "717459720227000000000" + }, + "0633ba746235d8fc8243751b1aa31646c299f262": { + "balance": "49000000000000000000" + }, + "06b6a5cadfe1fdc88015512e835d840e58ae4123": { + "balance": "1000000000000000000" + }, + "06d08fcbe96791514d900d2cb6c0f029d8d791d0": { + "balance": "19196168602258000000000" + }, + "06e5c0bad43a7011878b12a682abf01ccdfaa151": { + "balance": "30000000000000000000" + }, + "070656bb11ec36074d47c791c0b306394b703401": { + "balance": "1245216075291000000000" + }, + "070d84c938217163b60ed38e4937eea7158c03d9": { + "balance": "27507979787000000000" + }, + "07428c95ef3862026e54d3a963911cdc673dbcd9": { + "balance": "13792309994528000000000" + }, + "0781cb21df142ec5f67b956bf995f02f6f24985f": { + "balance": "224940990659000000000" + }, + "078c38c153b414cc4c12818fce2ea7ba09a34e51": { + "balance": "1410646136000000000" + }, + "07bfd0de7a9a1c927446aaf2d7ede55b471daa87": { + "balance": "77778844734000000000" + }, + "07d1ac83140188b8d7f4c8c607d0da22c5ba523f": { + "balance": "7713686418107000000000" + }, + "07d8ef8fc6dcde319a5af5b6cea18983bdc4c8fe": { + "balance": "3801361173000000000" + }, + "07dfab72dd3e44fcbb1ced625899bf20e0c52ffc": { + "balance": "154177778806000000000" + }, + "0817ce33d943e84c7b3261cc3e37b86b5d6d76ae": { + "balance": "90753785862000000000" + }, + "081be00a2ff62cbcb95a8eb020ac0efa33f93a42": { + "balance": "1027889807304000000000" + }, + "085ffdc6043b04653e51b1a34af20b609d158607": { + "balance": "3314058000000000" + }, + "0882c2228f5df24064bc37e8b7199199de308bb8": { + "balance": "98420617420000000000" + }, + "08918776e9a7136cedad5d0cee52f5d9dd833ece": { + "balance": "1263170315026000000000" + }, + "08bdbcff16919abc5e15fa68ece56eceef33f48d": { + "balance": "2552990000000000" + }, + "08fc8c7bd03fe1249266b233edfcf830693d0e10": { + "balance": "283490111768000000000" + }, + "090f79a4178b5180150444805f62c26cd21be897": { + "balance": "884195010282000000000" + }, + "0914a9dfc9d6ecc063a55e5320050112f305fe17": { + "balance": "15373898854941000000000" + }, + "091791ab5f8ab86f1d8f566ed221b268cbc55347": { + "balance": "2207516004000000000" + }, + "09529f8b1633ce4375451bb44b1025f6e5f9facd": { + "balance": "151697652792000000000" + }, + "09600bbb9d9b23661d269dbe1ed066d8e573b5e1": { + "balance": "802935104935000000000" + }, + "0964d2af1d5883fc0e0a77459f6a141824de7356": { + "balance": "9610980935058000000000" + }, + "097c731d1fc6792ac0f0ff92be4403c3749cc1dd": { + "balance": "165134479709000000000" + }, + "097f152b3f837d38ab1e8c0683d62f5a01d67902": { + "balance": "20000000000000000000000" + }, + "09a6772629ef0bf402ae6d27cd32e6eefb220a12": { + "balance": "20000000000000000000000" + }, + "09addb954d4e4b4e95e0c66d324115d609df5a99": { + "balance": "8435321515000000000" + }, + "09b2eb03e0fa321102196578eb40dcba3a46ec9c": { + "balance": "12723517962933000000000" + }, + "09ba0ed4dce470ba0bcb4d46b507c3b024b83070": { + "balance": "99999979000000000000" + }, + "09efbd6dfea375065be1b3a8f4541f024da21a34": { + "balance": "5870833623000000000" + }, + "0a24d4ae66edc7723bbb314e9b96dc7b9a31e813": { + "balance": "70000000000000000000" + }, + "0a3ee710909382f762648deff8ac7c8b30e2ce10": { + "balance": "407656462445000000000" + }, + "0a42b3145257154e76a97db8147c93be4cffd97b": { + "balance": "10000000000000000000000" + }, + "0a7cb6037f1eba5d19fe781335ecd37b7229c5f7": { + "balance": "74113322448000000000" + }, + "0a85d1bec4d44e309068b115abd517d3733fc56e": { + "balance": "14836218750000000000000" + }, + "0a891ba9ca7b99ecd815b1dcec9243dd4deff866": { + "balance": "178004857988000000000" + }, + "0ab69370b537d4ff5b45704fed293e46e3464f87": { + "balance": "1258876668246000000000" + }, + "0ab77a2e8ab9a3b659d1f1d37956c3607a0f9a60": { + "balance": "6283300817712000000000" + }, + "0abc7e8d52f5b0dcf1d4713e5fa4909102251dfb": { + "balance": "39307290577000000000" + }, + "0af2c0471c802d2e2b20aadf9dd4ec842d313ab0": { + "balance": "1529468143183000000000" + }, + "0b08c717bb3982fc8b92d5b3d30e5b41265a0d0f": { + "balance": "1184600154874000000000" + }, + "0b3f2aa9dc5b57b0469398d62f60a13774ec0e87": { + "balance": "132187640280004000000000" + }, + "0b3ffe85aa89e71a6e51e938c7265d2a7173061b": { + "balance": "2106793086824000000000" + }, + "0b468562e712d22b37e1a65f54b1fa825beebd1b": { + "balance": "15130906006000000000" + }, + "0b5333eb668dee29ebc0e335d74f33ffeaae9ac5": { + "balance": "125953133109000000000" + }, + "0b59a20107495e951b509187307782ca9dfa441f": { + "balance": "25000000000000000000" + }, + "0b8b7fb066601ea7744b81f6e1a21b8e489359cf": { + "balance": "101782000000000000000" + }, + "0bd7791097f79066d71ac0a2c94bce6628a63373": { + "balance": "44389955028000000000" + }, + "0be541af06e167206b5d1cbe08e8fb2b5ebc82cb": { + "balance": "9000000000000000000" + }, + "0bf178bba463f4f6c33e3d81b1a78d220f7b5d4b": { + "balance": "111784804790646000000000" + }, + "0c1f000249b1f1ac9e43c4f10e2da1cc2adf886f": { + "balance": "91251997635000000000" + }, + "0c74e46b115e19726997dd559d2b6ff1bfb79af6": { + "balance": "879229287149000000000" + }, + "0c7c1ec152c920a068c25626c22c4fae7f435536": { + "balance": "39849464104000000000" + }, + "0c85fbd7492d1ae87bf3d286c4750a34f1fd3121": { + "balance": "20000000000000000000000" + }, + "0c9fd6123e313f7d1f0cb25d99839102da08b2c5": { + "balance": "10000000000000000000000" + }, + "0cb051e3bdd9d96e667fbcc00a766d4f149f89e4": { + "balance": "1000000000000000000000" + }, + "0ce32bf6c433cbd26c6f09a1214db0374002784e": { + "balance": "3293279842000000000" + }, + "0ce6374ff04430e34edec8b6323feab2bccab92d": { + "balance": "93983447000000000" + }, + "0ce646412a1524c3f73edfd753c0ba3ee7338275": { + "balance": "10000000000000000000000" + }, + "0ced803e56eac3c99269ff7409d2d200d62d7c25": { + "balance": "49539379000000000" + }, + "0d1dc2be9f78ce2b2591e7f5b8af9dc778499bd5": { + "balance": "2235348003595000000000" + }, + "0d235583458a168e810275f907b5f87bebb2d1cf": { + "balance": "83106439283110000000000" + }, + "0d28fe6e8b7d4b8c90ef7c52b9656511ce5867f1": { + "balance": "1347248794799000000000" + }, + "0d5cbe0da660cbca831787efc45fecb20e06e02b": { + "balance": "297528508941000000000" + }, + "0d7a24f324176f6d793c3a2eb6c54d6ee47eca79": { + "balance": "25637813467000000000" + }, + "0da26a24e3650e84c52fedb36ef76225a8d9d259": { + "balance": "15467820321000000000" + }, + "0db7eaceaf3df21ebb49c56fa2d2e2c8e85dec52": { + "balance": "196000000000000000000" + }, + "0dde52823bd8fb2cb179e6d417c07ff285c31775": { + "balance": "347321514878000000000" + }, + "0df9585f1aa83189e0a813f5eac6e6e0b2bbb8d8": { + "balance": "2891430551000000000" + }, + "0e09af9368f05b476164953b7b9db60ac95248f0": { + "balance": "573644391203000000000" + }, + "0e18315dd2b663ce4859b5bed854403191452c2f": { + "balance": "24174325261000000000" + }, + "0e29bef5f4f66c38a0f05cca1e4938d57ff09c70": { + "balance": "10000000000000000000000" + }, + "0e443353b42e042ff5168e9b3c6de37070368223": { + "balance": "20000000000000000000000" + }, + "0e8cb6e439516b312158f169b546937b715db3f2": { + "balance": "8049136367000000000" + }, + "0e980fab23be601f3abec7b9a24e1335a5e765c8": { + "balance": "8301885845897000000000" + }, + "0ea63fef218ebf570a4ee62ef6ed712dbe623c44": { + "balance": "10000000000000000000000" + }, + "0eb60e3512336e4447ceeb8664ce0ecaa3eb0bdc": { + "balance": "41401696400000000000" + }, + "0edafc1058879a9568e711445b18ec4da31d2480": { + "balance": "10000000000000000000000" + }, + "0efce4565062b23b43dbc1e261463e363e4a5b4c": { + "balance": "10000000000000000000000" + }, + "0f08782c04bf7249ab08f4e251abc60aee792a96": { + "balance": "1380257622493000000000" + }, + "0f19bfe1eb24def828bf1be69791c63ba1de1263": { + "balance": "2223687184885000000000" + }, + "0f2171161eb9674218add261be61d18d86b846a6": { + "balance": "121000000000000000000" + }, + "0f42ef6c5690b6c95f8b8c9dbdc16f717c04852e": { + "balance": "81000000000000000000" + }, + "0f47f5063d321b34a0d800951bcdc3f53c07e32c": { + "balance": "5288516165000000000" + }, + "0f529a9beedb2c2a087a220f0013cea4f8454bfc": { + "balance": "114585457979000000000" + }, + "0f6751d10aaf855454a6e9e4241cfcae3b0ed732": { + "balance": "94160300063000000000" + }, + "0f6ed5a4c3ec100afcd59e9066ba7fcb63cfa6dc": { + "balance": "1000000000000000000" + }, + "0f77025519cc76c38e1cf0bd8721f4a5b9c814d4": { + "balance": "834620571043000000000" + }, + "0f773db2a1a96b775e4481575704f5f087b067e0": { + "balance": "554268361745000000000" + }, + "0fa0f73adbe82f8e09f8adbce15b051971e289c3": { + "balance": "11329911975000000000" + }, + "0fa6397d747d88a25d0c755b3be4eee0e3f68912": { + "balance": "31394936138000000000" + }, + "0fac8635a61bf7652d86725cc75c307949bd4f2a": { + "balance": "49000000000000000000" + }, + "0fb0ce787306ce13dcd614ab3d0e15d9772106ac": { + "balance": "50000000000000000000000" + }, + "0fb1d306d240360056f60f197dc7f68f732ac515": { + "balance": "20000000000000000000000" + }, + "0fe3571f498a6d945e123ac8ff6e3fed348d9432": { + "balance": "20000000000000000000000" + }, + "0ffd6b01ea9a7bd2576fe4a5838fe09e44c1639e": { + "balance": "100000000000000000000" + }, + "100bde3d73fda700369e78229127b58d2ade9177": { + "balance": "10000000000000000000000" + }, + "102028c7626970a28677bbdc7733484c8b14c2d2": { + "balance": "500000000000000000000000" + }, + "10245044be6a46ad41d4559129cb59e843379cf8": { + "balance": "857437279765000000000" + }, + "10417d0ff25c115b25915dd10ca57b16be497bf6": { + "balance": "10000000000000000000000" + }, + "10579870e6685ed7e97dd2c79a6dc3528bae968e": { + "balance": "5187866041491000000000" + }, + "109736465b4bbe31ea65ad01fc98f04498271e6c": { + "balance": "20000000000000000000000" + }, + "109c0535a4a86244c5094e99167d312a77657dd5": { + "balance": "138277702073000000000" + }, + "10aa08064689ee97d5f030a537f3cd4d8bbdaf74": { + "balance": "10000000000000000000000" + }, + "10d8bc8c3d3e2010e83009290586ad85b73321d1": { + "balance": "25000000000000000000" + }, + "10f22b82460252345753875555de2cebebe63a93": { + "balance": "765947730644000000000" + }, + "11111c3a2cfa55e52d6aacf533e1d412f8c8c01c": { + "balance": "4827254128275000000000" + }, + "11386103a0bf199db9504b617ccb3bbd780eb9fe": { + "balance": "10000000000000" + }, + "1156a129183e5bdfdf2bf7a70963285a979363a0": { + "balance": "10000000000000000000000" + }, + "11589cf70a6a4fbaac25224e2ddab222333f78e6": { + "balance": "49000000000000000000" + }, + "1162fabeb3eb1e4124179b00c4a1e01503023f54": { + "balance": "817145350625000000000" + }, + "116a7d140f4b7f9b4689063a8417ac07a32bae00": { + "balance": "106088220142108000000000" + }, + "116dc38ddb4b138b19ce6a51e5922c287da5c86b": { + "balance": "100000000000000000000" + }, + "1175f84f835a5ae40d49b8ca17e3e474c1eceef7": { + "balance": "1698584794716000000000" + }, + "119f822a796fee9c41a488949fcb14b589ffa628": { + "balance": "20000000000000000000000" + }, + "11a99f6019b6a53f5dc8cbd0c34f1ee75ced33b8": { + "balance": "102126982156000000000" + }, + "11b9324406068e8bf598d3a9ea59ef31c52f51fa": { + "balance": "6525925895203000000000" + }, + "11cb7be6869a10f5f9e8a47c6c92f729b083084b": { + "balance": "182959434323344000000000" + }, + "11d96a76166ec579e2b6cfa903f66da4af669351": { + "balance": "1000000000000000000000000" + }, + "11fcee55f78278df60f50096d45da1aafe72722d": { + "balance": "222859488179000000000" + }, + "120a1fc914718acd85bf92d9492330165d78075a": { + "balance": "3736627235906000000000" + }, + "12366136d83c77befdc30e04d4f5d808419f504f": { + "balance": "88008649003000000000" + }, + "12435af3f2f92ec43e8f2894be9c72fa932880fe": { + "balance": "1590548436852000000000" + }, + "124ff67125a00aed24e58b6d64ffa887a59b48a4": { + "balance": "20613022349070000000000" + }, + "1296f04910ebc89556ec7ab1b178fdbf14d0295c": { + "balance": "36000000000000000000" + }, + "1299f180e42bfaa1162d36110d29ab062e43e1c8": { + "balance": "321570118362000000000" + }, + "12abac62150c526866ec958cd0e3721b2c78d550": { + "balance": "51898545633262000000000" + }, + "12b345087cee385b9adccaaaa6741b767c82d7ea": { + "balance": "36000000000000000000" + }, + "12cd5e8c0c93f8e34b589b95954b719f54d1515b": { + "balance": "1000000000000000000" + }, + "12d262cdd25edc39b6fd9ae78184eb548e513927": { + "balance": "500976168000000000" + }, + "12d933448218629702c48547b3446b629ec65883": { + "balance": "2168010577395000000000" + }, + "13054aa42d3e119220ac359641c15f8b54bfffef": { + "balance": "24986834005530000000000" + }, + "130bda09f463a982199849ae617062a1d68f3a85": { + "balance": "154504844790000000000" + }, + "131a5da679863c05dc627d53634f2925ba0ce731": { + "balance": "10000000000000000000000" + }, + "1334f2752b5c21f681ba9e23a9fe95a85f8e05f1": { + "balance": "121000000000000000000" + }, + "13634512e2ae79fe3febb9e55e03b47bc350d7ba": { + "balance": "338331304738000000000" + }, + "136fae842aab625768bef9079ee1711e8c007d8f": { + "balance": "2759741687626000000000" + }, + "139fa969e8b74bee1f6113a362f15060ea998b15": { + "balance": "10000000000000000000000" + }, + "13c7e1d694bde6f8f6a31eb6c99f38dc739d61fe": { + "balance": "10901074773586000000000" + }, + "13c849944ad6ad12a46c46973d562bee8284f46d": { + "balance": "35114615504000000000" + }, + "13ec3aa8f4a427ecdecc7901060ccac9bea7a61e": { + "balance": "10000000000000000000000" + }, + "13f478c74acfd6897d13e602a8d362893f4fd038": { + "balance": "3521111850000000000" + }, + "14403970d0784a6458a7bf2584a53d14234e8860": { + "balance": "25000000000000000000" + }, + "14440bb7410337e34a064a92206075575f5362ee": { + "balance": "14918844868393000000000" + }, + "144a88e7a8af70b8bef5c4b70ee0cff771d0c252": { + "balance": "67961927542000000000" + }, + "146b79f474176a4b0069199b03669ab6467a4787": { + "balance": "122518123211000000000" + }, + "148893e7811c36c6bd1ada367681ab8327b3b2fa": { + "balance": "1313118418375000000000" + }, + "14900a17784e3b4d89d98b6cb31c74c685418b89": { + "balance": "131112181597000000000" + }, + "149a483758a98ffe28f7f25cfa17d7433f852ebe": { + "balance": "10000000000000000000000" + }, + "14a03c8e84f07c5596687a98d1e0b1859e9b34ac": { + "balance": "55000000000000000000000000" + }, + "14c7899cb34b5447d6363d4e8355113ebf4bcc66": { + "balance": "197554844582000000000" + }, + "14efa63ee285277c0f8e0d5cc22193e17984e11b": { + "balance": "106221254735000000000" + }, + "151c6099b3fb5b18e0e36a3335dff186dcd2904d": { + "balance": "4304311254087000000000" + }, + "1525dce233a971eb1387f130fdf0e5bf3455723b": { + "balance": "45004174531000000000" + }, + "15271904676f2bc2511294e500152d05ea9acc85": { + "balance": "9300898622566000000000" + }, + "1556ba42ea69d72c1d0faf802906645268e36aac": { + "balance": "171939212653000000000" + }, + "156558fb71ff986953d899c9916a121fd047675c": { + "balance": "290926338537000000000" + }, + "156fbf32614aac2cf462952ec1a3f141f797316e": { + "balance": "6084388860000000000" + }, + "15b9497d6bde8017baf3c29e12430e05a47efbf4": { + "balance": "206126229839000000000" + }, + "15d532e828bdcaf1246696d679a2eb66a154db5d": { + "balance": "69656571015000000000" + }, + "15e26a60cfaf23dfd9bbb999a30904d11b6ddd05": { + "balance": "9839303178835000000000" + }, + "15f3be2f11ee3b19472cc3d171931f050d8629a2": { + "balance": "13572825921000000000" + }, + "16254bed335420e5f793de2295b0081ac41a08d1": { + "balance": "144000000000000000000" + }, + "163aa91bc2ad588116141d48fcbd943985455cac": { + "balance": "618250979557000000000" + }, + "164cff4b9341d536b8aaf2d1dd0e3ed35ecb1db7": { + "balance": "671554639913000000000" + }, + "164d2a9a63868ac25bfe26ecba446d7ce256c351": { + "balance": "7233638188261000000000" + }, + "164e759b64d3ee0a23ec3030f50a1b454a6ec15b": { + "balance": "12281161499000000000" + }, + "165d4a0f23c016b8064adf0dcf7e31bc06350777": { + "balance": "256757922324741000000000" + }, + "166b862954dacddc3333aba4edbe523d693df858": { + "balance": "123677971414000000000" + }, + "16858eb1a6f0e7ff01b91aa9c92d0a433a5f767c": { + "balance": "500000000000000000000000" + }, + "168909a1c2a43cff1fe4faeac32a609c25fbd1e8": { + "balance": "62258808044224000000000" + }, + "16987ad8e10dda7f9e5d95c0f0ee36f46b10e168": { + "balance": "10000000000000000000000" + }, + "16b5dffce79573300a6514ace5f2e844d26fc64e": { + "balance": "5576805697087000000000" + }, + "16e01370a93befe24f6ae6076cd04c84cd3515b1": { + "balance": "1922179181578000000000" + }, + "16fbafd4fc871c7589e63062133793ab244c2019": { + "balance": "2865030627000000000" + }, + "16fdf76180796c6e4335eaa2842775b2e4a22e0b": { + "balance": "20000000000000000000000" + }, + "17081d4d6ebb9f4b163e181a59c2102c99fce6bd": { + "balance": "490625378000000000000" + }, + "17218ff455aa87b29ad4c4f7ba21e9c6f74fc97a": { + "balance": "1607392037652000000000" + }, + "1725bce47f3700f4646efb343f950e2e8ba66607": { + "balance": "58472967071000000000" + }, + "172c5f71aabf072507664471ebaa435779d74a32": { + "balance": "16000000000000000000" + }, + "173a065f351ee0513cfebfe9b950fd2c641fc8cc": { + "balance": "25000000000000000000" + }, + "174e1793c96cefb584ae0a67fff85c65065dafc5": { + "balance": "50221000010000000000" + }, + "1794bc4d622d514f95da5404358ed404b3f59aa3": { + "balance": "36000000000000000000" + }, + "179839d61e7c7a0382fe08e0573bcfbe42a108ca": { + "balance": "203299791419000000000" + }, + "179eb30b5b28a961eac70a919d26ca96e6472166": { + "balance": "55000000000000000000000000" + }, + "17be72168606fb5d27761157e48fc14789f84634": { + "balance": "311205588354000000000" + }, + "17cefb6611033759b8755197b983de2d7e98315e": { + "balance": "10000000000000000000000" + }, + "17e07cc7d89bcd1708b1f05ab6e1252c629d71cc": { + "balance": "903234908997000000000" + }, + "1811be559b657685c2f163122479101c404325b0": { + "balance": "10060694170000000000" + }, + "181417a4883c429ef26a4baeb48e70d4f00278b4": { + "balance": "4621446218088000000000" + }, + "181d345cd6b5f518bdab8d40f5d4896a725b3f3d": { + "balance": "114349561983000000000" + }, + "184625e544aa31552d2911023a892f739df84be7": { + "balance": "5303698708000000000" + }, + "185e4f6eee203ca3c089baa1e643ff1aab7cc8f4": { + "balance": "33969718257699000000000" + }, + "188e4a1a7b23ff35ec90b7bf7561db9e3c0f53bb": { + "balance": "394325508701000000000" + }, + "18a4dbf513be132f9ecfd69e3eb683d710e28c4b": { + "balance": "5997985132329000000000" + }, + "18c9298f62635ef47d0ef215b8a693af60829c27": { + "balance": "100000000000000000000" + }, + "18e7e2ee0c86bc1ba3595fee3d40257776fe8172": { + "balance": "185584626033000000000" + }, + "196575e74499b741877793f8c8facf2f3b1ddb8f": { + "balance": "30318381929000000000" + }, + "196df33f2d3ed473e6e07650419969f4a39fd03b": { + "balance": "15442864665000000000" + }, + "1975c5293ec9c72a28e6cc74173cdfd8de682fea": { + "balance": "103624886552134000000000" + }, + "19832cd1b2fc4138c8d9291a0f404d3c4326b48f": { + "balance": "4732362673000000000" + }, + "198705f46f31c7ca22f5b88fab210bc5b0c7647c": { + "balance": "548940869737000000000" + }, + "19a5a213e6abfee29f17e871222cbe9ac45322c8": { + "balance": "10000000000000000000000" + }, + "19ab9a7a4e9f9c08c9b4295c406b78389a864ba7": { + "balance": "369299839157000000000" + }, + "19f19f5f01b3f6a1c4f645dc7e3992b1196ccb7a": { + "balance": "97759406000000000000" + }, + "1a11a0b0081522e60e16f154e093ac2e005d24ee": { + "balance": "88024675252000000000" + }, + "1a27309b0c09be2234fd64afdbcfb099f8e2e7cd": { + "balance": "10000000000000000000000" + }, + "1a3d61754974bea23503a61ef0fe584b7b6e6cf3": { + "balance": "326950210355000000000" + }, + "1a49bbde1457a8d4c247606b206ac8d4d389da5a": { + "balance": "402438941516000000000" + }, + "1a7a4b41be64fff3a31eb6166db59741e073d0f7": { + "balance": "250000000000000000000" + }, + "1a8d282e82c606e992f69ce618ba634d98bf2683": { + "balance": "20000000000000000000000" + }, + "1aa0ba27662816e5e3d79e223cc18f5dfef089cf": { + "balance": "187581622694000000000" + }, + "1acab416a1d3e8caa65faca378c79aaf2065b851": { + "balance": "1000000000000000000" + }, + "1acd37af3f87da1dff743dfcb97038d178b1dc4f": { + "balance": "708034481300000000000" + }, + "1ad8f036022c3e5258455d6aa05fb4be5dd121b1": { + "balance": "42957064709000000000" + }, + "1aee811e06c579c21fbcc3b53d2dcf9d5f24808e": { + "balance": "52480060284048000000000" + }, + "1b03b7a4e9908c3531618f49f8d050ba6afb4de6": { + "balance": "28410489187000000000" + }, + "1b073d026e93de51db34d5a8e19047784c277ea1": { + "balance": "20579129628026000000000" + }, + "1b0b87e414bc8fe4920fe104b6de7d17db3a1a19": { + "balance": "10720000000000000000" + }, + "1b411c692c80948e59cd805a0f8574dd67519288": { + "balance": "5416615538000000000" + }, + "1b8d57e995749618c7bb3e60194ac6fc57e9b3eb": { + "balance": "10000000000000000000000" + }, + "1b913efde1255516346b40ae2a48ebf62251682d": { + "balance": "100000000000000000000" + }, + "1ba7276c133f93d43db2f2caddec08e0167eaf15": { + "balance": "82559173174000000000" + }, + "1ba919f7742160cabf2756eb6eae67b92530f3f3": { + "balance": "1102304139883000000000" + }, + "1bb20857de494694fe15bd11f8cac1218435fbc0": { + "balance": "10221322567000000000" + }, + "1bb5c5e81d451f03e899852edc8556a9f7aac5df": { + "balance": "18781017696028000000000" + }, + "1be3507349ed07d3e7902951d490f560a75e96be": { + "balance": "660691718918000000000" + }, + "1bf1c0b2e6f64b612f35f2bf98d894b13dda9bf7": { + "balance": "3050161851140000000000" + }, + "1bfd3c2ba6a537e97cedd542cd554a5050963d54": { + "balance": "20000000000000000000000" + }, + "1c4af5003f9e7223f4141107d21640e4a85a4827": { + "balance": "612390629874000000000" + }, + "1c6a94810bd0afcf79ceea11afe86c34f6813211": { + "balance": "10000000000000000000000" + }, + "1c7e277460191c886cb1647173d27122c2146252": { + "balance": "209062806527000000000" + }, + "1c818ffa9caa61d512fa5d7d6e566f3ae37d5434": { + "balance": "454897845316000000000" + }, + "1c9599d5f8e5eaf8f68d35d52132e15a153f6d3c": { + "balance": "36000000000000000000" + }, + "1c95ab5229fd08c638a1728c022f09291b8dc55d": { + "balance": "20000000000000000000000" + }, + "1c962808c175ee5e5e365483d066c8ea95993700": { + "balance": "1362779746855000000000" + }, + "1cafad295b2188f10192c8a32440931f7e3554e4": { + "balance": "36000000000000000000" + }, + "1cdc2899ec563d79569d1ba776bc03cff331e786": { + "balance": "572163587827000000000" + }, + "1ce0042e7b4f13589f5f8490836dc63e0ca60c3c": { + "balance": "25000000000000000000" + }, + "1ce62051fd7801d294bf31a7b44cd87510e8b545": { + "balance": "2008112411284000000000" + }, + "1cf20f30cd901b2e5fef3f948289dafaaabaa77d": { + "balance": "1444000000000000000000" + }, + "1d449764d38b7a4ac848f49e2dc99df02dfd8a53": { + "balance": "48215693645000000000" + }, + "1d635125c494b1137ca5f15ac95dd6d93c3a9546": { + "balance": "10000000000000000000000" + }, + "1d85a61353c3e0b6d34e105e35c8c7833b6a1e35": { + "balance": "16000000000000000000" + }, + "1d969134ee156c41c98c3721c5dbb092c0b581a6": { + "balance": "64000000000000000000" + }, + "1da12434596a9c318dab854f06d404fe61f0a69d": { + "balance": "16675185416000000000" + }, + "1db0d23fb63681958a66e716e99df3e0b848fd12": { + "balance": "1103581911968000000000" + }, + "1dc628820da657f07ab5eb887d5f512378b5b61f": { + "balance": "6272287019755000000000" + }, + "1e05cba75b0dd379037940352e0073564957b7d9": { + "balance": "12453446342664000000000" + }, + "1e13f037a92ab6f19c4484ae3301b3ac6f48575d": { + "balance": "106244918916000000000" + }, + "1e167bc07f094915c00e7aa4c43b607ed2c998b9": { + "balance": "1000000000000000000" + }, + "1e1f9409bf92c3ef59aa2fd82dce55cd90e23f19": { + "balance": "99139000000000000" + }, + "1e4dfea7871d941e72a161022b62fdb01818c86d": { + "balance": "81000000000000000000" + }, + "1e5d0b525228167334e94314a201388bba08153b": { + "balance": "1138044078759000000000" + }, + "1e6633290c9898abf5fcac54396de770164edc5a": { + "balance": "25052055674000000000" + }, + "1e76296584058670ea80fe9a39d8f457c03747c5": { + "balance": "10000000000000000000000" + }, + "1e88b2c8dcd289929e51a15c636d0b0f3b035569": { + "balance": "87357600832000000000" + }, + "1eb59a1732a159a91a9371650943840e0eb61174": { + "balance": "20542821429000000000" + }, + "1ee077bdef6d45d491602342cee008cd1e2912e3": { + "balance": "10000000000000000000000" + }, + "1f1ebf2f80afced68424cb7b0b966fdf42d508a4": { + "balance": "1460082126494000000000" + }, + "1f3d4a903bd32a537efae19592f5516698c95a20": { + "balance": "10000000000000000000000" + }, + "1f6431696efc6f1ab98dcc2ef0e8553da697e6f1": { + "balance": "20000000000000000000000" + }, + "1f657552b745acbdf731f2ad107d6362480abc88": { + "balance": "162042599568000000000" + }, + "1f699a7682c1266291a3f49e19cac0846470abf5": { + "balance": "712268213248000000000" + }, + "1f7a332dabb00851705274c59187817d859cb9a4": { + "balance": "199999999160000000000000" + }, + "1f7c333047e168f5d3408c42a4919bd44b8f7961": { + "balance": "3918096658000000000" + }, + "1f8226f7a4525b9f3cd4da3acc1bb34529f8d28a": { + "balance": "3530829464000000000" + }, + "1f8b6fcea9e0991ad0b0b25dc65748518a28713f": { + "balance": "142069368416000000000" + }, + "1fa3de6913e4de78cc4828e246554785950c3c8e": { + "balance": "178437291360000000000" + }, + "1faa75d57fd597d2b58d2ac6f65bc2bd5946911f": { + "balance": "13092024644362000000000" + }, + "1faf1721dba3266cde1e04a7e9c789bdabdd930d": { + "balance": "862698523071976000000000" + }, + "1fb861559361701fca1df6ab4ef4d2fb9d2d7e13": { + "balance": "100000000000000000000" + }, + "20154d678cdde9ca1c0acb94726f26617a4da0d8": { + "balance": "2288800561677000000000" + }, + "202484a46ca9d54d0d456bc38e2a74ec5f469349": { + "balance": "50178714107336000000000" + }, + "20324278018b4d8e0c49e0fd1be35d3494079165": { + "balance": "484082383314000000000" + }, + "2033ef68ef6297e9229bb73e6486330543aa3eb7": { + "balance": "51260669473000000000" + }, + "20b1e0ab7b9d62a314946b55a5775f24ae3cfa00": { + "balance": "1540424185584000000000" + }, + "20b61f2eb5e18b1e8568d18235918f9e2f596c32": { + "balance": "10000000000000000000000" + }, + "20ed8ca39dd148edf22e03b8021af32cecadd42a": { + "balance": "20000000000000000000000" + }, + "20fd5feb799fbb021ba262d28332b4dda8f44a2c": { + "balance": "7607475714434000000000" + }, + "215ab8aad1c8960838225294d086f0786c2dd796": { + "balance": "19929201327000000000" + }, + "21681cda53aa1a4cfb3e3ea645c8eeaecfc3ba4f": { + "balance": "10000000000000000000000" + }, + "217b75eaf2c0be12108120ba56ddb709e1885324": { + "balance": "36000000000000000000" + }, + "21be1d75b93e96017f088f1ca64ba7076c8edf07": { + "balance": "150798073752000000000" + }, + "21ccdbe0216b486cb39c94ed13767aa061c75ce9": { + "balance": "11070639373000000000" + }, + "21f2289f2d274bddd7928622fffdf3850d42d383": { + "balance": "268859368544000000000" + }, + "21f54f92a7d9a91915e1751ceb02cb8e3ed3d622": { + "balance": "10000000000000000000000" + }, + "2202c70ec23f4605394d69944edd9f90e488eb61": { + "balance": "9000000000000000000" + }, + "220e2253e1ab9ec348cc28d38bae4cb2d5d9cf8f": { + "balance": "116100821631000000000" + }, + "22328e434957107884854999e666ad0710187e3b": { + "balance": "233364805270000000000" + }, + "22851c0487d119ee3f150010515358d6ff14807a": { + "balance": "104464221701684000000000" + }, + "22a38000f5eca29001e387b52c18fb6030683fac": { + "balance": "55000000000000000000000000" + }, + "22b655a19810307750ed1b6b093da10a863d4fe2": { + "balance": "11840799203605000000000" + }, + "22cc48cf48e8ee207bc08411240f913a4e594529": { + "balance": "10000000000000000000000" + }, + "22d6ea6cb8a9206285ccddd3b6d0d1471ba66f17": { + "balance": "64000000000000000000" + }, + "22e2f41b31a0c69472a1a02d419886539b7b6197": { + "balance": "39885451304000000000" + }, + "22e962f91d01480d027ee0060030f529a7a64c8f": { + "balance": "93285203531000000000" + }, + "22f169328fb1104b386ad7fa69f0c7bf3e9a7d3b": { + "balance": "63366769386000000000" + }, + "22f35f5e0e7a8405714de66a5875c7ef84ec4891": { + "balance": "60944382032000000000" + }, + "23041bdc8d371dc29ffc890f19317dabeef12634": { + "balance": "402327389857000000000" + }, + "230eff5e8595f418686737ae671f1f1d225080a5": { + "balance": "114574598112000000000" + }, + "2331e1756d9800800fc9b54ee6e43e1150b6e58b": { + "balance": "44594796226000000000" + }, + "233a72b132e4ab1d3884274d4402d1a2a6399f0b": { + "balance": "1372148909093000000000" + }, + "2369d9dbbfd0f8aa8a3d84d8f2aea840a0cdf760": { + "balance": "500000000000000000000000" + }, + "23754e5cef31ab60aa97a0c8f9ccb4f2969f2d6c": { + "balance": "24764775861000000000" + }, + "2387973589fb07a8c1ec92492c0b8ba9ab5e52a2": { + "balance": "11642113696681000000000" + }, + "23950cd6f23912758ebe9d412166e27994fe6ec2": { + "balance": "100000000000000000000" + }, + "23b383e11573f3ca9be84e1e11694f58a432324b": { + "balance": "206558238838000000000" + }, + "23c329bb641fa51122ea476e3bc614f5d4f9cf00": { + "balance": "35908627324000000000" + }, + "23cb9f997c39853486adfc1a8b029874d1a6af15": { + "balance": "1400984459856000000000" + }, + "23ee14215c531f6ff1baef2c74b1754306f4532d": { + "balance": "10000000000000000000000" + }, + "23f641f765cf15665b6c28d77229d3b2a58fd857": { + "balance": "266570948120000000000" + }, + "23febb49d9541360b9d099377df16b5630dfbb52": { + "balance": "228513797641000000000" + }, + "24082040652a09cbed6504f3dd6491e0ee9d2bff": { + "balance": "91160839809000000000" + }, + "240d3edf4aaf42e99d366ca36d82c370271b8e8d": { + "balance": "65535355843947000000000" + }, + "242b63ebf47678f17c176d5d4a670e46e66a823c": { + "balance": "469668185647000000000" + }, + "2433612fb939236a87a97261ff7b3bc7b754afb1": { + "balance": "20000000000000000000000" + }, + "246bb03a3fab572b3c64fc23b03dfda42b7ea34c": { + "balance": "936364046000000000" + }, + "246c510dfaf5b49bc0fe01c8256d3879c1b5f89a": { + "balance": "100000000000000000000000" + }, + "24bf4d255bd3db4e33bff1effd73b5aa61ae1ac2": { + "balance": "302436106595000000000" + }, + "24c0378e1a02113c6f0c9f0f2f68167051735111": { + "balance": "36000000000000000000" + }, + "24cf04b7450a0fac4283fa6fcfef6215274b273e": { + "balance": "83714002622000000000" + }, + "24f5f8e7d6a23b55c95fcdc1300de05f9d2abd83": { + "balance": "20000000000000000000000" + }, + "25204bfb27a08dbdee826ad6d9c3398ec6d14fe1": { + "balance": "5929256591480000000000" + }, + "253d95911b4174805d13706b449879413b1672be": { + "balance": "37012901440000000000" + }, + "256065f7e919c508b68957b1d2c9120d29181e12": { + "balance": "25000000000000000000" + }, + "25624542c14c2ecb9a0fe7daec9ac5af16868ee7": { + "balance": "16000000000000000000" + }, + "256d05b6de445179e504a6c94ce1253ae159e19a": { + "balance": "12048598744001000000000" + }, + "256d37fc8980a969063b1f7e7fda8b87d4210da6": { + "balance": "107293553721000000000" + }, + "2588af91a0e8f3ba3ab636781bb84e263acd1f52": { + "balance": "8910000000000000000" + }, + "259774584d4fcae1d84f5997c00beee8a380e46c": { + "balance": "1140713354605000000000" + }, + "25bda1418853a22eb6a5380e8a2862d2a74949bc": { + "balance": "10000000000000000000000" + }, + "25cafdab7f79f7b95d55b4c2dda1f4080aa74d64": { + "balance": "2525573681000000000" + }, + "25cca69b41bb51c51b387c47ece83f30b9a78daa": { + "balance": "163449631440000000000" + }, + "25ce9dabd0a72b02e0056931155ba99c94cbc837": { + "balance": "230073284349000000000" + }, + "25d9d1785c96acddd926b3ed52987ff74f9083f6": { + "balance": "780460361789000000000" + }, + "25e56bd3e1461f27db4eb0cce8bb5ca1574401f8": { + "balance": "1001937531200000000000" + }, + "25fa2162d5c86cda10e4be42c14a24329e455ad8": { + "balance": "50000000000000000000000" + }, + "260a932a23b344056acb8e676714ffac0a13ad2b": { + "balance": "2000000000000000" + }, + "2622efe8836095fcf48d9c8019f48c8320d6e0f2": { + "balance": "5451866545636000000000" + }, + "262447c4d8826ed23ea25e9703a11b4ad3ae9388": { + "balance": "33992454005000000000" + }, + "263eee3badb9b0dd13579c09361806503705a1de": { + "balance": "1134831344000000000" + }, + "266f4c232ebc946c46979cd90d70868380e186d8": { + "balance": "20000000000000000000000" + }, + "267dfe6fa918686942f5e1d19d5fa615f6f2086d": { + "balance": "3569373363935000000000" + }, + "268ad2272c2b71243a7391020a600fd8dfa42d45": { + "balance": "122768017414906000000000" + }, + "269e4f43be9865f05a277933c2fbb466659ada7f": { + "balance": "22064992930948000000000" + }, + "26ae161c20acb26a320fbfbd60c97335cda28bca": { + "balance": "170710653621000000000" + }, + "26b4da905780fb0c5c3e7e5315989fed3aeef135": { + "balance": "20000000000000000000000" + }, + "2704312aa5a4202f14fa3b08e587e4f0ef13accf": { + "balance": "124259630994000000000" + }, + "2704e4b0e8df0c1f298843109ae3bb26c29a22c4": { + "balance": "3155521256785000000000" + }, + "2709347d12251c01aac6455108c6bebe72f0af2d": { + "balance": "220898650215000000000" + }, + "270a32b41dde877463d2106ea4f4529557a5e1d3": { + "balance": "10000000000000000000000" + }, + "2738b3746d6bda9bd72858eaa76f8b5ce7a88c8c": { + "balance": "10000000000000000000000" + }, + "27593d2271aced83e81034e8dd603d098238320c": { + "balance": "20000000000000000000000" + }, + "2771ba4b5944bb12d74b1888255c60e0db215fd2": { + "balance": "412946979808000000000" + }, + "27780086136ea3e97d264584d819dcb2176d7544": { + "balance": "292224348060000000000" + }, + "278936fff8afb553043f038c39fe93906bdb1f4f": { + "balance": "1448466441752000000000" + }, + "27aa0d45d3506f8446816e0e2e9675d46285f6e0": { + "balance": "20000000000000000000000" + }, + "27e655dcc5728b97b3b03fb2796c561090dced1a": { + "balance": "9841344000000000" + }, + "27eb0529279f7a71e50efb70bb1767cbe1ffa4ce": { + "balance": "10000000000000000000000" + }, + "27f564956c837d7949739f419d6ac99deb33d790": { + "balance": "1505247707018000000000" + }, + "280f5618a23c41ac8c60d8bef585aa1cc628a67d": { + "balance": "1316618646306000000000" + }, + "28167a591d66ae52ab22a990954a46e1555c8098": { + "balance": "1000000000000000000000000" + }, + "28257eeb8d20f2fe5f73b0ff2eca3214e30ece4f": { + "balance": "95924728584000000000" + }, + "2827abfc49828db0370b0e3f79de448d46af534e": { + "balance": "769862008499000000000" + }, + "2832b92434e3c922206c2408442bc8274606cbd9": { + "balance": "103421320914027000000000" + }, + "2854f190a38e9b9c04cf499259c6577a68b0b5ed": { + "balance": "144000000000000000000" + }, + "288923bd91be164496e5378ee484f0e4c6c16ed6": { + "balance": "10137243270703000000000" + }, + "2897ff80794153edb721801fb91c6d8373c965f4": { + "balance": "10000000000000000000000" + }, + "28aa06e2290010374097aa2f87a67556d8d68083": { + "balance": "84783245638916000000000" + }, + "28b04ec8eb18b0c6a384f9d92cfb44d1d43ecb51": { + "balance": "14364248730194000000000" + }, + "28db0c000cad3a524bb68dfdd74ffd47b42fb13a": { + "balance": "43586590410000000000" + }, + "28ecd4c5fe98cff66a5b8423f4a27cba9634e2d0": { + "balance": "56106658052000000000" + }, + "2930822031420731f09dce572554a8b8c1eaa09b": { + "balance": "1170839742000000000" + }, + "295154c4522d7bcb2e24b7de9c543dcd1c5f51d9": { + "balance": "179028680906000000000" + }, + "296be4ef7402b00d7af673c1770a50162d7ab602": { + "balance": "8206640005889000000000" + }, + "297b84150756fa89101dd59750a7beb36fb8785c": { + "balance": "1168894124400000000000" + }, + "297cfb72cd1b8b2808fd1b25cdcf7d8de279ad96": { + "balance": "500000000000000000000000" + }, + "29cec0eca9f8508a1ba192a90bb6dee18c40745a": { + "balance": "260217025084000000000" + }, + "29d8f7e72bfa297f17fdce9cf8f4a398f547e200": { + "balance": "307787433251000000000" + }, + "29e14b01c59ba894dd090382fb193ea441164b90": { + "balance": "229028661439000000000" + }, + "29ed634e165084b720e446d28893dbeecd6a7018": { + "balance": "226530464200000000000" + }, + "2a0f8136d43248233f652fe579ef3bd2281dde24": { + "balance": "4007544428000000000" + }, + "2a10204a0c7c9f7701e33c1b71c9427ea16e2e45": { + "balance": "50000000000000000000000" + }, + "2a319ee7a9dbe5b832beae324290f7df6d66f516": { + "balance": "28127560161000000000" + }, + "2a50bfda2b06a9fb28c73f14aaff4f7ef865db65": { + "balance": "10483823413828000000000" + }, + "2a7b7feb145c331cb385b9fcb9555859c16820f6": { + "balance": "1017182951264000000000" + }, + "2ae076c36b18a60f1e3c05d434276a1e16f3f838": { + "balance": "10000000000000000000000" + }, + "2ae2e51ea2ee6a848acde342db2bf6eac927e5af": { + "balance": "494279795271000000000" + }, + "2afd69fac54c167e7ca9d8198a8de386f3acee50": { + "balance": "227162683047000000000" + }, + "2b08018d6e65a7b74ddb5ce1af99976a484b9f50": { + "balance": "16000000000000000000" + }, + "2b0c1d629ad2958ab91e31f351a91219fdbca39e": { + "balance": "113239399820000000000" + }, + "2b2bb67fe9e44165d2108676579a9437c760da30": { + "balance": "20000000000000000000000" + }, + "2b2c99e88e938d1f1416a408a7d0041a605bce16": { + "balance": "6118539729000000000" + }, + "2b5c97b6402ac189e14bbca3e7759319ca8a9222": { + "balance": "10000000000000000000000" + }, + "2b813339c7f818f578b45f17c41c7e931c7828e2": { + "balance": "842834712955000000000" + }, + "2ba6fc21f743968d51f80113aadfc0fdfe8499ed": { + "balance": "309973507270000000000" + }, + "2bb75b272b279cb54498f12b6805261af643c8b1": { + "balance": "1426727673809000000000" + }, + "2bdac062364abd9cf67ba7de214a2cceb0511033": { + "balance": "1090525272063000000000" + }, + "2bea658caa805241aa921f48c8f10cb49e16ffae": { + "balance": "1295499213027000000000" + }, + "2befe7e34299a7c1ad53fc9988ce77e2d9fab20b": { + "balance": "4326342236300000000000" + }, + "2bf466a83cd44aaf0f627606a1c954fd31deb782": { + "balance": "1388986370166000000000" + }, + "2c016a23890e9633fc17b0a8d328ec1ec7ee0113": { + "balance": "92483174342000000000" + }, + "2c45a87a63cc5c8c102d12b83bd9a3501ee41995": { + "balance": "394657687589000000000" + }, + "2c600a596368405e584f3b869f7fabef4ce54aa4": { + "balance": "9879984853585000000000" + }, + "2c7032da8b7816e16095735aee43d1c3f1c43acb": { + "balance": "10000000000000000000" + }, + "2c7275511fe06ee86663b3a618497168b35b0cdf": { + "balance": "10000000000000000000000" + }, + "2ca4074843e9519265447c0dd9ac84ddc2033c1a": { + "balance": "179612279567000000000" + }, + "2cac03ba2c45a6c8186bdceb095b7c5feced3114": { + "balance": "2022060470376000000000" + }, + "2cb8c2cd506b2d7b4cac88ce63230022d412c62d": { + "balance": "211378154058000000000" + }, + "2cd27561cf37ec229982dd592c71d1aab9c2d7d8": { + "balance": "42189968284000000000" + }, + "2cd2e85310a4fbb7f296c3d0d1cee07b191239eb": { + "balance": "1940327317417000000000" + }, + "2ceca4501c5f2194518b411def28985e84d42913": { + "balance": "25000000000000000000" + }, + "2cf7abd42394634689aa2a36d263a6345116b7df": { + "balance": "3553167226295000000000" + }, + "2cf88f29356c166df8383d3312cea10397e25150": { + "balance": "76961677759000000000" + }, + "2d0b62fe49592752cfebaa19003a60b8b39b1cb9": { + "balance": "10277397502735000000000" + }, + "2d2051887107bbd8ed45b405b9be6974a13172d9": { + "balance": "1928781992000000000" + }, + "2d2c9525e2811f4d1016c042f476faf23274aa31": { + "balance": "1000000000000000000000000" + }, + "2d2ef9e1c7a6b66d9a2994adb3ac4a9921408e69": { + "balance": "10000000000000000000" + }, + "2d3bcd18e5c97ddbf1cd28ab37eabe070e9a04d1": { + "balance": "323879852538000000000" + }, + "2d3e60496d0092a4efc665389a916be1a9f8b378": { + "balance": "161958437779000000000" + }, + "2d3fb0ae9b17d3a57d23549ae5500fbb163de25d": { + "balance": "25000000000000000000" + }, + "2d8106dbee6f728c0ff11887690a6370a7d9f5a5": { + "balance": "3102418708000000000" + }, + "2da48eeb788686811ac8270ef3baf0159fc47446": { + "balance": "252187695395000000000" + }, + "2da9d2a6f0b92651a36b05c5e9d2a717c6e166de": { + "balance": "500000000000000000000000" + }, + "2dad81b23d8447190259119019c04a4ef61ab91f": { + "balance": "53428719965000000000" + }, + "2db1faf35901e272aee74a2469a278fdaa6e6e18": { + "balance": "100000000000000000000000" + }, + "2dbae8e1ad37384ca5ff0b4470d3dbc73559841c": { + "balance": "10000000000000000000000" + }, + "2ddf9e23945c181b8592d7965e782068b4c38b37": { + "balance": "100000000000000000" + }, + "2def05d1f2abbaa193a219b87e5319c7ecd48dea": { + "balance": "51359946957000000000" + }, + "2dfd221f96a21e41ffe4dca67b15cd352fe9637e": { + "balance": "36000000000000000000" + }, + "2e1371fcfea9d8dc8e692897a91753400caa9c3a": { + "balance": "5199902650733000000000" + }, + "2e2e04945adbfaeec698ea0f5275f1ad5ffd3d5b": { + "balance": "42034514567000000000" + }, + "2e41f865cfbcf8b89f848405e04de9114087f4ff": { + "balance": "44875730962000000000" + }, + "2e530254768ce94db0ef1204ede0e12b3558e7eb": { + "balance": "14319506377747000000000" + }, + "2e5c43868f45de268967fb22f3f4107da401510d": { + "balance": "20000000000000000000000" + }, + "2e5d2e117d2ba9af9697ec023a4d10b5a2436902": { + "balance": "16000000000000000000" + }, + "2e6000778fb225ddb3e1a2f297d56774e85d9c9d": { + "balance": "10000000000000000000000" + }, + "2eb64b8ab13f0d7823158217d15ba310ed3d0e58": { + "balance": "58724606000000000" + }, + "2ec3973ff33a06d355ad4e8f73b657af8a5ed8e9": { + "balance": "1165606294808000000000" + }, + "2ed4362ea5edf510e210af733089b294f87e8f67": { + "balance": "427561040806000000000" + }, + "2ed8788f1c31b508e37079098a7337bff77b49cc": { + "balance": "10000000000000000000000" + }, + "2edbbe1e2ea482920c76a4ff4c14602b4d37c955": { + "balance": "294409476945451000000000" + }, + "2edcba2bd76128750c8aa00f832c62db30aa7868": { + "balance": "25000000000000000000" + }, + "2ee5abcc0d0d51d4b18947b5aaaa95d037be4e2c": { + "balance": "20000000000000000000000" + }, + "2f058187ef141c06c7c87da86cc1953d2fcf70fa": { + "balance": "9000000000000000000" + }, + "2f16b101da9986a18f4b0d30a26557860338c4e0": { + "balance": "254907899725000000000" + }, + "2f4363df2c61273d230071286bb0157dfefee2cc": { + "balance": "64000000000000000000" + }, + "2f6099a8cb7bc3713b87dab20994d8dc09342003": { + "balance": "1902400000000000000000" + }, + "2f7b3902ce56f74adb0f83cc7d3a99df440cca1c": { + "balance": "825246221388000000000" + }, + "2f7d0298ff6a363375b7eecfe754fca0963c8a1b": { + "balance": "101000000000000000000" + }, + "2fb7c16232b3b1f2e3a676d6d5c93ae6fe5cb14e": { + "balance": "1000000000000000000" + }, + "2fbd5ccc716d2f510d10ec84def3fa69e49f46ca": { + "balance": "1000000000000000000" + }, + "2fd84376be11772e5d072cd74c96b0d9a49c27fb": { + "balance": "1000000000000000000" + }, + "2fdab070e20e2c8923a24c196bec72c33ff0f220": { + "balance": "64000000000000000000" + }, + "3003e6007f69902a0f5e4b4e6d0468277897fc70": { + "balance": "1501210602075000000000" + }, + "30095e6a4ccd1ac2014c3d1d98dce003d775708e": { + "balance": "500000000000000000000000" + }, + "300e47e0fa556371f6c882eb98423be44de7c239": { + "balance": "9108837665958000000000" + }, + "3011231224920b62bcfcbf0aed4fde35dd0a4bdb": { + "balance": "374689586073000000000" + }, + "304be24debce62e70943efddd20457d34e85ab40": { + "balance": "81000000000000000000" + }, + "30912555bb14023e9b7c90aa2314721918cdf1f9": { + "balance": "10000000000000000000000" + }, + "309a94ca7b44bc84a7909ee2b93ed1c94eaf75a1": { + "balance": "39000000000000" + }, + "30bcc93965fa36bbaabcd781326e42227c4e1a51": { + "balance": "10000000000000000000000" + }, + "30c71fed91d24bff69f286ff8f0c6c02a21736a8": { + "balance": "409782329653000000000" + }, + "30cbaf4103757013fd8fb71c44a985939e212b86": { + "balance": "7424807960947000000000" + }, + "30dd59e66093d0bfd87b09c5f6588b9857e9a6f7": { + "balance": "26123006239871345154" + }, + "30f692235f254b02f583d5b515f4701a35c7f692": { + "balance": "148184457997000000000" + }, + "310763019a24a927ce42b00604ee664ca53ff6d0": { + "balance": "393757908273000000000" + }, + "3118a5d4d06ca8b7c8835f4860e6973228000ee2": { + "balance": "56188713212579000000000" + }, + "311adec5bfcaed44680691cc644ee120a484aa05": { + "balance": "169000000000000000000" + }, + "3124e387aa7023995643344c782dac84b9d8c7d4": { + "balance": "1393596696367000000000" + }, + "31379702391cb5a737db3f3ffc336bd03aaa181f": { + "balance": "10000000000000000000000" + }, + "3145606c3ccbaf337610185ffac14ac4f0583c0b": { + "balance": "196454968572000000000" + }, + "315e11501d2c57a62af1631fc2662d4d8745401e": { + "balance": "225000000000000000000" + }, + "31a785ad3eea177c59fb575cad0b44f9a48a12e9": { + "balance": "38039017162416000000000" + }, + "31ae64035e95c1205bf957afb5e1636df00dea3d": { + "balance": "1718600907000000000" + }, + "31c0bb22fd2e9d22984f248a16ec3ed9ad834517": { + "balance": "5982762676000000000" + }, + "31e73a3b5451ebe1163571e9e0567c425bbbfb83": { + "balance": "10000000000000000000000" + }, + "322543c74039ef61fd051021b5e6f16b54bc7c1c": { + "balance": "101346282441000000000" + }, + "3233c7ed11c25bfc41d506c3ae0daf5a3c7c1278": { + "balance": "20000000000000000000" + }, + "325dae17b5225f6734a02c677d43fd126bea89b7": { + "balance": "365067246683000000000" + }, + "326ce8166a4094b93c15557f50f2b1d47811e72c": { + "balance": "16641460224765000000000" + }, + "32cf76046ae48b609524b1a6203eb6296d04853d": { + "balance": "1094839482061000000000" + }, + "33456a28f36aa240262cf95b78b4ac2cd8aa77f6": { + "balance": "3077123488326000000000" + }, + "3348bce2ef90ffd6a59ef5079e1af84b2dd604a7": { + "balance": "9000000000000000000" + }, + "334e5f0ae77dcd3d32dfc2c4ec6ab5e2826dc4b1": { + "balance": "3176777762079000000000" + }, + "335775e19200cd0305e529bc4cdf7295a47cb2d3": { + "balance": "2945631571804000000000" + }, + "336ba81ea6ec4f0da38c1a1761ed3d97fd3ca28c": { + "balance": "3587379203826000000000" + }, + "339191e03e9d5a08ae7b58f4c860235a0721b5a1": { + "balance": "2732237722000000000" + }, + "3399bf9f94c5488c89450257b39fdf3ec8c7f413": { + "balance": "477423805836000000000" + }, + "33cb8556a6c6c867e1be7de591cb22c1b7e9824e": { + "balance": "62293494164000000000" + }, + "33ed633804f39367078e830328dd223254be3366": { + "balance": "22842013797896000000000" + }, + "3409025dce86ad441a5a80f30ce03768d37e40bc": { + "balance": "1381667933000000000" + }, + "34153174cd4d3f1eaed7438638d302f6414d5965": { + "balance": "50000000000000000000000" + }, + "343c6b82b13f0dc82d4269e2c806d2d58e6dde35": { + "balance": "9546969736042000000000" + }, + "346089ea81f7dcb79caf2444df34bd6ee78be4bb": { + "balance": "4344080889000000000" + }, + "34984a8f96dbbfd1f977826a4c2187482559a2e4": { + "balance": "25000000000000000000" + }, + "34a5cce96d2211feb04472260c4cd368bda8432e": { + "balance": "1240050112677000000000" + }, + "34c026a39e44955d1051e8669f9cc58a027455c1": { + "balance": "20000000000000000000000" + }, + "34d730652f4aa002a9f39a47212ca3bc47506b8b": { + "balance": "418050617956000000000" + }, + "34e1d8c8a32ce0f6378abb9bd05ea1f9bfdc5782": { + "balance": "20000000000000000000000" + }, + "350b228870445141f4417ea5dba4f009d693b96c": { + "balance": "76995849736776000000000" + }, + "350eaec708d5d862831aa31be2c37b2fdcef97c6": { + "balance": "258753545822704000000000" + }, + "351fc1f25e88b4ccf090266ebb408593418d8fde": { + "balance": "10000000000000000000000" + }, + "3523ac7a6e79162bb8400bf161cb59389432aa51": { + "balance": "436606776923000000000" + }, + "354d490095e79a29bda2fa11823328450f14333b": { + "balance": "50000000000000000000" + }, + "355a555a36e319e76042e62875a15e1db3012b86": { + "balance": "20000000000000000000000" + }, + "3568840d0a26f39248ab088653ede831f150ce29": { + "balance": "16000000000000000000" + }, + "357096b9c1c7c8d51b682ed3c43d150f55629ff2": { + "balance": "900090781248000000000" + }, + "3588c47ba9204b672c456ee9b5c1ae70f3c738ac": { + "balance": "10000000000000000000000" + }, + "3591edeb9c036e871b4fc6fb3ae6d42e0c0d7203": { + "balance": "1000000000000000000" + }, + "359d92e3e8757a4a97187a96d408c0c11f5c7eb9": { + "balance": "22330509101591000000000" + }, + "35aac2a948f316ba93ed111ac127e29ee9a3adb0": { + "balance": "364387817746000000000" + }, + "35b20459a7daa5f44ae423fd4a1b451ea5090b09": { + "balance": "20000000000000000000000" + }, + "35cdaa84c1f3bc2673bc0c60222c133bae0d3db1": { + "balance": "15234182435000000000" + }, + "35d554233ca690130aaa43501e121a208c657226": { + "balance": "10000000000000000000000" + }, + "35ed399940ece44d01ac873b9c0d3212e659a97e": { + "balance": "55000000000000000000000000" + }, + "35f164612afc2d678bb770f317085ae68cce19bc": { + "balance": "693763596328000000000" + }, + "3601b36cb475101d0d0976a8de9d38e5f3483a08": { + "balance": "1000021000000000000" + }, + "361368bc42c8daf365bce9f9ff3b611373d7b690": { + "balance": "21658400518000000000" + }, + "361bc43be077a269e3e37c11e91479017c47f847": { + "balance": "268900383140000000000" + }, + "363c7a2203f6f93287de091bde3c87eb6800e7a7": { + "balance": "20874859005000000000" + }, + "365dc06856dc6ef35b75b1d4eabb00a7220f4fb5": { + "balance": "30000000000000000000" + }, + "3660e246bce68e2b6e4a802681f188587d2c1c99": { + "balance": "55000000000000000000000000" + }, + "366868ef8e193d7e649ee970d476e6774d5ff1ac": { + "balance": "2544456626840000000000" + }, + "366f7f762887cfb2d09cefa4a5108cf390bdeb41": { + "balance": "26837527714000000000" + }, + "36759f9c92a016b940424404de6548632c8721b1": { + "balance": "1033159825798000000000" + }, + "36a939be88508646709d36841110015bf7cedd90": { + "balance": "144000000000000000000" + }, + "36adca6635db6b00d28a250228532fe560127efb": { + "balance": "3370820618318000000000" + }, + "36bfaed8099ee9f216193ade26d21656c98ce4b5": { + "balance": "1353728563832000000000" + }, + "36df854d0123e271529a8767d1cded4e7b5f31d6": { + "balance": "10000000000000000000000" + }, + "36f59989a902cd10725ff7fe2cab1689aa4e9326": { + "balance": "20000000000000000000000" + }, + "370d6999ae70e781c81d12dc259ea304183b01eb": { + "balance": "45563989086590000000000" + }, + "370e8af59a64a3171b422913921a1e2f982dd512": { + "balance": "170263356254000000000" + }, + "372e01083072214134018f50bde3c8ac4f6e071d": { + "balance": "1400474252324000000000" + }, + "37410fda52f94185d824258ad5f3c9ad9a331257": { + "balance": "11830521097085000000000" + }, + "3752b7e1628275522cd693307787b9564501d959": { + "balance": "67839627078000000000" + }, + "3776f82701c384ce6cbf6a8fea40772cb882b66d": { + "balance": "50000000000000000000000" + }, + "379f63e538168925ba6313f9a6a3b6e7f0e8ed52": { + "balance": "292876625446000000000" + }, + "37a24c1a8080ab429a136c5582782b276eaa931f": { + "balance": "6099055841000000000" + }, + "37abbeaf24b5e6264c87633734852e243d377010": { + "balance": "1051360014489000000000" + }, + "37c50cecab8fe9dcd81aaede95050d27c53f4d45": { + "balance": "106051638566000000000" + }, + "37f82962c3097f0cd9ff605db66e792025a540cb": { + "balance": "10000000000000000000000" + }, + "382ba1e6c53cd7b9c360ef894962d281d557561f": { + "balance": "216789631461000000000" + }, + "38309b458993916efc1ac8b0b5d41302fec21095": { + "balance": "999139000000000000" + }, + "3847f25956a97a32caac059fd9e4cdc105756e25": { + "balance": "876497264905000000000" + }, + "384fe5f399638d277d4fb93f26d527497939287a": { + "balance": "280035151914000000000" + }, + "388335d8b92b445b1b19a3107547bb6ca7c0763c": { + "balance": "167140942784000000000" + }, + "3894a1e65973a542101caa4dc01e9553a5521d63": { + "balance": "34262479791000000000" + }, + "38a0e019c6120a19acaf0e651dd8338982cdaab1": { + "balance": "153843170492000000000" + }, + "38d4bdb10819a7d4ae9a32f4abb82402dff319b5": { + "balance": "1471131830647000000000" + }, + "38e56a55e2ac8320a480562f4a7cea9220306ee3": { + "balance": "907464312139000000000" + }, + "38f18123f3643e03d24ad759afbefc90ed923a2a": { + "balance": "943729130798000000000" + }, + "38f764c861def6d5d65e5ec099536f4cfcc3af55": { + "balance": "20000000000000000000000" + }, + "391e12b51fc85fb879a72fa746ca06c7a5659e6c": { + "balance": "9000000000000000000" + }, + "392342dc7b475c3975877a41622be0fed8e386be": { + "balance": "218719857770000000000" + }, + "3944cc960b8f1993ad841629a79e53d0535a88c8": { + "balance": "210571656485000000000" + }, + "396219864b8cfb0ffb3c675690ccd7026424ad4b": { + "balance": "138984508746000000000" + }, + "396403f26b388150b4876485b124a49845101464": { + "balance": "10000000000000000000000" + }, + "396f1e0f9e7ee86d1b2159ab8f9353677d12d340": { + "balance": "121000000000000000000" + }, + "397cc9f6254d56c721c767e41628a9078bea878c": { + "balance": "225000000000000000000" + }, + "39878b0c7049fb179aba0015279eff6cc3136816": { + "balance": "33962071375000000000" + }, + "3a06b58d0cceee5b091fe6aeb0fc0db5774e9395": { + "balance": "272033811720000000000" + }, + "3a3330e0152d86c5aa1d9bdfe9e1697645d3377e": { + "balance": "2165107207206000000000" + }, + "3a3bb8ed3130e09fbdfc21db3571d4711fc92d60": { + "balance": "885484658836000000000" + }, + "3a4ac96c489c864765cb1997a0084ba745b67a87": { + "balance": "1257436384863000000000" + }, + "3a69e1c351978ced418cea6cee019f220bcb065f": { + "balance": "579242822216000000000" + }, + "3a76a23d81929bed05ef7e1982d32b456e62aa7c": { + "balance": "1027078338792000000000" + }, + "3a7beadd1e11d0e3326c0dcd0f670530612931a5": { + "balance": "20633069818937000000000" + }, + "3a867c44d0dd06517a82ad467d0aefd7f11ce729": { + "balance": "12323708574000000000" + }, + "3a969ae486215e24c7ab89e38929562e2f85d923": { + "balance": "18955477335000000000" + }, + "3ab81366d898a8b798afb08a4b722ab0eb883652": { + "balance": "1220090012596000000000" + }, + "3ac1e14ed5929d382f6488c5444e717373ed29ba": { + "balance": "2030543873000000000" + }, + "3accf4b8ef20e4fea983f13f99ab257a5f9e988d": { + "balance": "156030342415000000000" + }, + "3ad38fa6e3c078025794e213d9dcc5aa397050c2": { + "balance": "36561766773000000000" + }, + "3adef81b2c861ae39c418d55be99aee2306e29fc": { + "balance": "15752410974000000000" + }, + "3b21ff4d5801d3976643899f195fbfd1b72a50b3": { + "balance": "8971221745646000000000" + }, + "3b2f2635dd428ac0b5873088a9a81800f09d6e02": { + "balance": "56922872447000000000" + }, + "3b39919c7bc8d0afec792288c56ab7f4934dc7d2": { + "balance": "1678237240464000000000" + }, + "3b468d9d5546810aa837c29ccb8349548b0e8170": { + "balance": "1100000000000000000" + }, + "3b83d1b651f117f1559a19b04ef408619c2dc4a7": { + "balance": "53628552066000000000" + }, + "3b9a72201bb1e8e678e36129cb1570e3ac99270e": { + "balance": "25000000000000000000" + }, + "3bcab1535b04a0a3fbb673bc41fedaa80bf7901c": { + "balance": "1526488015042000000000" + }, + "3bed42c3d0c49ffac87b9d482f6338fdc9e3880e": { + "balance": "26189771073000000000" + }, + "3bf736b57f0ae47f3714a6bb852090a543b9d367": { + "balance": "652395174320000000000" + }, + "3bfd481651956105ed909eeb98be404ec5ae77e9": { + "balance": "177520143473000000000" + }, + "3c0a12a327545b5f8b7b5c1f7a1ec6a341ec9578": { + "balance": "4184342789398000000000" + }, + "3c132698d59927fe08cba433a41d08acc96c0edd": { + "balance": "151913899135971000000000" + }, + "3c27bd92c4be1a19974ec773bd20b13afe582c9f": { + "balance": "10000000000000000000000" + }, + "3c2ad171573a608286f1fa3f5ef9e6099823983e": { + "balance": "3240802189194000000000" + }, + "3c4b5e808b9fb8baab1144b981b6cd53e216fcdd": { + "balance": "61214114118619000000000" + }, + "3c4cfc6ead044819ceb41c1c64ceda1a228af801": { + "balance": "9000000000000000000" + }, + "3c553afd45535f7c2a70c701d00890e607b96ffe": { + "balance": "2678827200938000000000" + }, + "3c620d55268c55b6deea3b7dc7f59dbe93b6e141": { + "balance": "55494311990000000000" + }, + "3c9b204db23b902d4295e6aba3405917efd59449": { + "balance": "55543672571000000000" + }, + "3cf233b7730a175d05a861318b7bb917bb5bee06": { + "balance": "1867187351033000000000" + }, + "3d292272992397ed5f27d5202da693128d023d35": { + "balance": "79770828413000000000" + }, + "3d353cfe84e9a93aef90547fbeb6e4b4bef83069": { + "balance": "36000000000000000000" + }, + "3d54da4ddd0621822a114581ecd15572e6488be9": { + "balance": "1623867132383000000000" + }, + "3d62ddc67d366fb055eaf92c936a6e7df5085454": { + "balance": "124933526793000000000" + }, + "3d754df1151b9b62a6ed48b477225121c29af063": { + "balance": "50000000000000000000000" + }, + "3d79d1ebd5224ffdc13e27924ab7f9f8e3452ec9": { + "balance": "520163228474000000000" + }, + "3d84fd9785a6bd3148847038c6f1e135042a892e": { + "balance": "10000000000000000000000" + }, + "3d9574c3860f30bcb42523a0cbb08aa7dd83e733": { + "balance": "15259904884000000000" + }, + "3da809a5911ccc77f892034049a97a9022c35e7d": { + "balance": "1415009021101000000000" + }, + "3db9a6c6ab3d0cf6d3bd7e04bdad39b4d419ab13": { + "balance": "9999781764000000000" + }, + "3dc7367c3218f88de8867c425f89102d2f2056f4": { + "balance": "10000000000000000000000" + }, + "3dd273dedb28824d1309c7d60a0744a6b6353e79": { + "balance": "9000000000000000000" + }, + "3dd6b25eb91dd2f3468e0786e8beb465abe7f515": { + "balance": "275172962210000000000" + }, + "3e0d14f83b304136311a33bbac2720c0cd66f117": { + "balance": "3390479675000000000" + }, + "3e15e947ee76f52f0f2a7d84da7c4ab060eb5cbf": { + "balance": "6751398714759000000000" + }, + "3e1b5469e1da4ec27537513f4df3f1a338a7dc2d": { + "balance": "161899580000000000000" + }, + "3e3329bcc90e47e4dabb5c93572b18b5e0efa024": { + "balance": "10000000000000000000000" + }, + "3e5a585ad0f34d78899433edaed574af052616f0": { + "balance": "910225655353000000000" + }, + "3e6be9615713bb06198bf354ef434a9db649699b": { + "balance": "783525278181000000000" + }, + "3e7c7c082f2f99b1ad579400a2e93586a24ed992": { + "balance": "159035553843000000000" + }, + "3e86ea5713f90022c0914fcc25e97c39487eb957": { + "balance": "101867287023438000000000" + }, + "3e9dabab6e50a696edfba6bcd44230d087c8d04c": { + "balance": "3315882414579000000000" + }, + "3ed956f86fe78223c86e164e4f372c9a0bf4a279": { + "balance": "119959915220000000000" + }, + "3ee87e776fb12e9c894e36fd5a61daa984e8a5cb": { + "balance": "50000000000000000000" + }, + "3ef1c8f294443f776a794563ce7569a8fe4d5d20": { + "balance": "25000000000000000000" + }, + "3ef6a396d6611df6c79ec1e6ad6bbd253917fbe9": { + "balance": "10000000000000000000000" + }, + "3ef727346fc631ae6473e9a36e2e5e54df696195": { + "balance": "121000000000000000000" + }, + "3efdcf2c0998637cb82d2b5fc24f27162578d207": { + "balance": "1054861112990000000000" + }, + "3f2cb2335e2bc07744175c497e2f437e87c2a146": { + "balance": "48999979000000000000" + }, + "3f4d16663a4f76ade93eb8bd6ca8fe2158e24322": { + "balance": "237117597268000000000" + }, + "3f7c5e6aea7f3f74d764df50f0fc1aa758fc99a7": { + "balance": "63372222562060000000000" + }, + "3f92239fdb41c6ec228252248c2db3f23675e275": { + "balance": "28538064487000000000" + }, + "3f9610454b621c04f00f01f4d54046502edb21fb": { + "balance": "16000000000000000000" + }, + "3fcfb30cbfe53c0c43f58c28386d9a6e5b49f7cd": { + "balance": "79080579219000000000" + }, + "3fda0c9a3d3f0000635376f064481d05d1b930bb": { + "balance": "10599947385000000000" + }, + "3ffce0475430de0bd9b09a412e404bc63aa28eea": { + "balance": "10000000000000000000000" + }, + "3ffe7583b568448ded5183e1544bca0d283680d2": { + "balance": "1076944549283000000000" + }, + "4003a137e577351a4ad7e42d1fc2d2cf1f906b6f": { + "balance": "25000000000000000000" + }, + "40215fc4c6300d8d8179383d9028fd2d909c6cc4": { + "balance": "3941346079000000000" + }, + "4027d7bbfa5d12c1ab9d08933a1659ae8dd023ee": { + "balance": "1156537386462000000000" + }, + "4039439c960070394dbda457726d97121c7b3669": { + "balance": "4444061364102000000000" + }, + "405978c24a12d930ada6163a44fc4a70c16569e1": { + "balance": "707298643296000000000" + }, + "405d2c1b55ba3f67c8634456b99c19092b407a10": { + "balance": "1462185951849000000000" + }, + "405ddfcf45005cf5a0ee1bfa605a7346a0167945": { + "balance": "88775535985000000000" + }, + "405f72a6940acf5d36a29498075f2d0d7a75bc22": { + "balance": "402796678569000000000" + }, + "407253b005ae97857f812fc60d441e5367b4bac8": { + "balance": "1484147810895000000000" + }, + "4091e1fb1c7af51a64c201d6a0c8f0183dfb7ca5": { + "balance": "10000000000000000000000" + }, + "40950bad9580d4b111955da7d3f9ada26dd9f05a": { + "balance": "500000000000000000000000" + }, + "409a28106192cae7a76c6aa8add0f73dcd63d2c0": { + "balance": "214832616721000000000" + }, + "409d5b872b059aea9a773e854f9a17ed2d5c2ef3": { + "balance": "64000000000000000000" + }, + "40a6e3c753b04c42fcf89cc30df8f50418caecb8": { + "balance": "754228409494000000000" + }, + "40e5ce1e18c78d6c792f46ed6246bfb31bcdb6af": { + "balance": "500000000000000000000000" + }, + "4121692a14554ddca1ca662fb577d7152d4fa7d0": { + "balance": "49000000000000000000" + }, + "412acb10c8ca937ddd64cf0d413b1dd34760f72b": { + "balance": "6073360870000000000" + }, + "4166a5c94d5ae48ced410f950d40656182bf8990": { + "balance": "55000000000000000000000000" + }, + "41752b7d0d3ee58a6b69d8ba721c0894ff701237": { + "balance": "585556720807000000000" + }, + "417c86d6bf734e99892a15294230771bbfd7e1e1": { + "balance": "38233258264000000000" + }, + "418414498f7361b29428c54732e1f49fb394f813": { + "balance": "2063786326155000000000" + }, + "41a424dcbff6bf31686f5c936e00d21e8a4e0f78": { + "balance": "33554754580438000000000" + }, + "41a893429d5f8487c1866b87779155d4bfe33198": { + "balance": "20000000000000000000000" + }, + "41bbedd607fa576d130305486824cd2871bf6b05": { + "balance": "649728993301000000000" + }, + "41ee42c1fb1bcdc9c7a97a199fdcf9b63623521a": { + "balance": "7906012418597000000000" + }, + "41feffaf56d1712af6965fa6eee1b06bd624e7b8": { + "balance": "49000000000000000000" + }, + "42107e765e77ea76b3d6069d3775bc3aef7d692c": { + "balance": "25320783684183000000000" + }, + "421f4dab3240e15a1c78e3ce8642de9b578b8e4a": { + "balance": "832511242936000000000" + }, + "4246c52c3601541a873d4bbaafedf28b9bad5b73": { + "balance": "10000000000000000000" + }, + "424efe1ba28bb1aeedc38a3a5135547d0fe80751": { + "balance": "25622294162729000000000" + }, + "424fb0a3ec325bf42e7edbef7e450f2ffd1cf318": { + "balance": "20000000000000000000000" + }, + "42714c04d17f6c29029daf7f50d1cbad6590cfad": { + "balance": "271755674161000000000" + }, + "42b66e9123d304b70fef3dbcfe8587fd6189b5c4": { + "balance": "1030481609000000000" + }, + "42dacbc412b829cb304ffbc316b3f81b379bfc80": { + "balance": "304208485736000000000" + }, + "42e3d9832c8b6cdea39c97525570391803dee276": { + "balance": "2581020833000000000" + }, + "42f757898f95c1b46f64a4a6b7f86ab03022d672": { + "balance": "100000000000000000000" + }, + "42f7956fd659e00d3be2f3d1d4f3ed187aef04d6": { + "balance": "50000000000000000000000" + }, + "43160b2bc00f7f8f7fc806e2f6e2ffdc62b3a651": { + "balance": "1000000000000000000000000" + }, + "431b77cdd067003eeed26c1aee32f67fb94f7092": { + "balance": "902514849986000000000" + }, + "434e44583786e354731bca250d94ef0d8860a538": { + "balance": "1187789683866000000000" + }, + "434f1b9b193c88bf58685124aac0167fe69f9014": { + "balance": "500000000000000000000000" + }, + "43535982688844fa703cb9bd5723790cab364049": { + "balance": "100000000000000000000000" + }, + "43559f405590592c254e427fa25f03e774d8defd": { + "balance": "6913200000000000000" + }, + "435c08c481d59308a64afec0d6f936321bb120bf": { + "balance": "9005920819593000000000" + }, + "43629748a92b846f21f637aac5103412fbabb9a6": { + "balance": "1177513692845000000000" + }, + "43650b37552882d225ccc977aa2b7a86a4ca9bb1": { + "balance": "16000000000000000000" + }, + "4385de394371d26a45f18e8b3842effd015027bf": { + "balance": "187331693412000000000" + }, + "4386ff9648fe420503c9a36fe7b97c079de3b770": { + "balance": "2714401478778000000000" + }, + "43b370c4457cf39a3be86cc00c2b27614ca6e638": { + "balance": "8316429850934000000000" + }, + "43c464ea740172fe6f4f09974106fd24029837b9": { + "balance": "129643160399000000000" + }, + "43ddd2d33dcb7e578f4e59ad6b9c61a24c793aa8": { + "balance": "500000000000000000000000" + }, + "43e0f8065eb7faf3bbd13bc7c5d5d8f5ff1bdac3": { + "balance": "4454324716300000000000" + }, + "43e96cd065d7934b246d0fec8cd2dc6b36d56d7a": { + "balance": "81721481452000000000" + }, + "43e99acabfbdc6cafee3afb12fa7ed1345370b2d": { + "balance": "4595292398872000000000" + }, + "43fae764c7555b859b93d2126edfa59cfbf298b5": { + "balance": "105746805109558000000000" + }, + "44052eb938c02776b5240f38ec99f5ef51ef0d87": { + "balance": "38446396949881000000000" + }, + "440fc7621cc17f121f0bdf2a68c5be2c3af4fd3b": { + "balance": "1026958147051000000000" + }, + "4440ccbc77249a4d891d9ab5a5f2026b17aff7c7": { + "balance": "10000000000000000000000" + }, + "447f3f702c13a3fbdc8675c6285702b5aa2b66bc": { + "balance": "1089533398014000000000" + }, + "448f152be153fdb0497403f70e37d876946a5021": { + "balance": "614429461682000000000" + }, + "44a1e3a044f5d1fb00f4beb3772a3ee08d8b7093": { + "balance": "1000000000000000000" + }, + "4515edc7154bedd7143b69a04c4e738f8aa4ab18": { + "balance": "10000000000000000000000" + }, + "4572148fe5ea9d4795e1f1ed93097aac1d70991c": { + "balance": "2873782218000000000" + }, + "457581f223b8eacd757abb292613e317d6f59305": { + "balance": "3582446780073000000000" + }, + "45b450961882850f7038d5cdcd2a8fa2dc4b5469": { + "balance": "20000000000000000000000" + }, + "45bdfdf3840d4341503cd7fc87e4b66f6179e5fe": { + "balance": "10000000000000000000000" + }, + "45dd9baa87b3c94df66308d8ed4f3a5bb65c3dcb": { + "balance": "25000000000000000000" + }, + "45de332b8ee95d886cf11b99291b46f46c1ddd45": { + "balance": "36000000000000000000" + }, + "45e06afdbc70288a2cc55bccc4fb2d8195aea028": { + "balance": "790360485306000000000" + }, + "45fce5fd1acb2bc5507723c897cb340437e39735": { + "balance": "100000000000000000000" + }, + "46045cddd940d80596826ce5354489b3047663bb": { + "balance": "100000000000000000000000" + }, + "4610286f8a2649dcfbc6d91735745f418a6abc75": { + "balance": "10000000000000000000000" + }, + "4615905ecc6f7df0ccb7b86a3e1d3770adb2f874": { + "balance": "1000000000000000" + }, + "46256e00ff927d54b0ca139ddccac2148784273b": { + "balance": "10000000000000000000000" + }, + "465e40e7d129ad310fc60ff0f17c0f968611118f": { + "balance": "677971225649000000000" + }, + "4664a920f7fe9b0d78a665e1a4aeb95f287d6059": { + "balance": "20000000000000000000000" + }, + "46679de1c6143138fd9c44ff05853a52371915ff": { + "balance": "363627463229000000000" + }, + "467cdc210ae48ba99740d37ee79fa57c4216bc81": { + "balance": "10000000000000000000000" + }, + "468053d193debb88735571acb24b764f2676272e": { + "balance": "49000000000000000000" + }, + "46826d1f1418abbe4e7b9236d643e5b57a0f0208": { + "balance": "37752144164916000000000" + }, + "468df2ea57972ddeb88d470a5f3c2c0e2284ac17": { + "balance": "757320434551000000000" + }, + "4695ad5b686520ee8426d24b50ff7a5f0d703443": { + "balance": "2851082366000000000" + }, + "46a149bc8ec2b85fdf938f753a6c53777dcca2b1": { + "balance": "10123698633000000000" + }, + "46c081440f760a21b74c2499bdda13aef8245930": { + "balance": "180752319265000000000" + }, + "46e615324f6e4fb242f9bfecffc0c802ba7733c9": { + "balance": "10000000000000000000000" + }, + "46eb54f09dbdaaf3b97a1f79a3d82ee2e902b3b8": { + "balance": "3430510380318000000000" + }, + "46ebb24b04919ec0164f0bafcebca2309f2d3035": { + "balance": "129155832233000000000" + }, + "4701f9fe78111011f820fe28c47522e601655678": { + "balance": "9000000000000000000" + }, + "473f44e2c1d5d7aa53cf7041d7ad19a0d9eaf1d8": { + "balance": "162679637505000000000" + }, + "474f8bf4d03a7efa4d190905ce062eea7c75118c": { + "balance": "1259904506149000000000" + }, + "47598330e862a4f7cbda8be74ac10cd5d370a55e": { + "balance": "291763101699000000000" + }, + "476455d48fc858a06bd7854fcf1bd60bcfde9ed3": { + "balance": "10000000000000000000000" + }, + "476826d58192ad822f4686311d6c6d4d4f66ee5f": { + "balance": "453184657722000000000" + }, + "47a231eb3fdbc24f2d008f06228624b2a45ae5fa": { + "balance": "1000000000000000000" + }, + "4805f4c0eb1c83c436118ec9148019e5fc1962e3": { + "balance": "1311161626928000000000" + }, + "4809f373cdd56c8481ba3bce5a401d55a7e50a50": { + "balance": "2284845194349000000000" + }, + "4839bf9ad56873abd5695057bf71972806cde827": { + "balance": "42264923611000000000" + }, + "486dba2a47decabc9a85d1d64d74687983ab273b": { + "balance": "49000000000000000000" + }, + "4877993f5ecf02451f8d4591594cb2f30dcf9f26": { + "balance": "100000000000000000000" + }, + "489723e325f609e27be14528c4111fb3eec13f7c": { + "balance": "2489030141000000000" + }, + "48b710d16e9da736b773453089d69cc6ede116b5": { + "balance": "26651593216000000000" + }, + "491088e1e4b5c3d65870f2deef9be6ec3dc6c7c7": { + "balance": "1446809481953000000000" + }, + "49174451320ad2e14cb2b05ecdd251b75ace3038": { + "balance": "15533380764616000000000" + }, + "4956ead915594b621131b2fc2dbbb49ba43c5559": { + "balance": "1175638488000000000" + }, + "4973a0d32147ba89f525a18f989518dcfce93b0e": { + "balance": "522848489444000000000" + }, + "498c6f9063705054fae07260d4176c3d55a97650": { + "balance": "732941433000000000" + }, + "49991f68f2a76bd1cffa3e9721be36f3fd8351b8": { + "balance": "17742568700019000000000" + }, + "499a8af194e0040f349e893937fe3858f8267fca": { + "balance": "80704784160862000000000" + }, + "49bcbb7b263febf54f8ff498525bac8e7241f966": { + "balance": "603044032468000000000" + }, + "49bd72773656c4e1a4d16284aea2fb05546d2b31": { + "balance": "1896607093054000000000" + }, + "49bf80fcdefebe8cd4830ba09e46ccd7231c8e6f": { + "balance": "10000000000000000000" + }, + "49c9d82dea78f14b1c52efc0196b67f508f7859b": { + "balance": "2313040051399000000000" + }, + "4a2daeacf0468d137e3bc464d6d5fa3893a9136b": { + "balance": "10000000000000000000000" + }, + "4a6c428f245e8d9115b34764bab17eb86ac472be": { + "balance": "10000000000000000000000" + }, + "4a6e8d037acf1960dbbf14e7a02fec0ac656f9c1": { + "balance": "6516295825438000000000" + }, + "4a7e7fc3c72f2f9b92bf0dcd5297cfb19f077d7c": { + "balance": "99426213999999999" + }, + "4aaa6817a5000bb7596e000135b3051c5931e7c5": { + "balance": "102884105546478000000000" + }, + "4abcdc3d7d3d314a163da92aee53a56b87313a2e": { + "balance": "44402243657000000000" + }, + "4ac9385ade2377b061f4211b392ed6a6e7fb83cc": { + "balance": "1000000000000000000" + }, + "4ac96e1e26cb66ff788ed8c62db811d7b4fdbc74": { + "balance": "68476115774000000000" + }, + "4b10c247caf33fb872d9bf86572424410aa86752": { + "balance": "337915294240000000000" + }, + "4b23ffff1894df49005c7afc0828880924571299": { + "balance": "169848767272000000000" + }, + "4b486895caf3a0b5afa198df744de7082eec8666": { + "balance": "1672587376054000000000" + }, + "4b98fc960610573be456c0e1e319f4f863bf9095": { + "balance": "259382246718303000000000" + }, + "4bc0cc483be20223f40ed6deef63dd9645c216c4": { + "balance": "4322684620000000000" + }, + "4bf4a046afdd4ec9d0e50730ff6ded5ef2327442": { + "balance": "70601389160000000000" + }, + "4c0149058e2e74f7c900e6d6e5fa12eea882c5e0": { + "balance": "2017399852886000000000" + }, + "4c17a3997fb70599794d01a33a27a6d5b52b6f01": { + "balance": "469002093209000000000" + }, + "4c4559e7b32340dce112cf7a021ced1b113f6dd9": { + "balance": "25000000000000000000" + }, + "4c4f02b3f232b8ce8485d425639271510cd0486f": { + "balance": "68828038140000000000" + }, + "4c52ec56142bb6e8c8830e5c17b01b5165915f3c": { + "balance": "321766233041000000000" + }, + "4c58defa57875e709ca039a54a2be5aed6672f6d": { + "balance": "121000000000000000000" + }, + "4c5a886ab90b6bac68677a7eb92a06bf33ff2930": { + "balance": "236899852150000000000" + }, + "4c60539363edbd812334a54543c40ecab8af2ac8": { + "balance": "3281904094699000000000" + }, + "4c76897d0d5d39195354194710c5e7f99bef63d1": { + "balance": "10232271258305000000000" + }, + "4ca3c03780c20a64f3b5ebb75669982a71ee8a71": { + "balance": "377172198976000000000" + }, + "4caf77eefe062a6f053a464171bc75254b47f52b": { + "balance": "20000000000000000000000" + }, + "4cb7d7ce805e56f6e47e94cd755b6d97f8f996a0": { + "balance": "120998866000000000000" + }, + "4cd34f8f3299d3b7aaee180baa0b432369e1b3d6": { + "balance": "197088135242000000000" + }, + "4cd7aaa5415d809f405f520e4c0319a6029b981b": { + "balance": "686627368232000000000" + }, + "4d01067555f1ef63883f25c562b07168f79fa80d": { + "balance": "17547055698000000000" + }, + "4d2cb4c1da53e227b08c0a269402e9243a13f08d": { + "balance": "324000000000000000000" + }, + "4d38bb5f48ec37b751d16de32a4896fbda479ce1": { + "balance": "802530078718000000000" + }, + "4db3e76e2f68896cecc9826e10a5e09df0352c28": { + "balance": "555180306924000000000" + }, + "4dc8730d9f032d33dc493bcd3c6375b38f41afff": { + "balance": "5726933881000000000" + }, + "4ddde96556f5185a13617f01ebd9102800bc9e9c": { + "balance": "1181822708402000000000" + }, + "4df9359cb204bf649668ff8086a7f5e24709083c": { + "balance": "262998978400000000000" + }, + "4e0a1a3dff0d33c418758263664b490140da9e01": { + "balance": "100000000000000000000" + }, + "4e0dd6d8de5caa3a3bf9fdd6f2d7b30618623cc0": { + "balance": "10000000000000000000" + }, + "4e11af85d184b7f5e56d6b54a99198e4a5594b38": { + "balance": "76658631121000000000" + }, + "4e314349abc52c686d47dc771ebc8040966be386": { + "balance": "632341985941000000000" + }, + "4e3fb09c35375106bece137dbe0e5491e872871b": { + "balance": "153648535396000000000" + }, + "4e4f0d606f7065bfb1545e60b5e684ae1934e055": { + "balance": "48998635468000000000" + }, + "4e50957aa6c91c548075add8ec625b74c0973abd": { + "balance": "1000000000000000000" + }, + "4e5c6efa76493f0e9422582016aac50539ae60d9": { + "balance": "2078967343000000000" + }, + "4e70bbcb50c4e875fd573dcb694908abf3b30b37": { + "balance": "20000000000000000000000" + }, + "4e7f5670a7dd168a0803e53b8bf72f4db280e3ae": { + "balance": "1658463113665000000000" + }, + "4edaf859990a10977bf378df45b32f93422c84b4": { + "balance": "121000000000000000000" + }, + "4ef41923a1a426772832d3c267adbd84e5994edd": { + "balance": "5432615017384000000000" + }, + "4f11a70d80f36f46ed0c2a5fff1a39b711f3bae5": { + "balance": "8415785077000000000" + }, + "4f159095afcc75b8f5cfc90c9d07a0d77ac8ed69": { + "balance": "25000000000000000000" + }, + "4f2652322736cc18b24af582d4022fe329a9dfb7": { + "balance": "9000000000000000000" + }, + "4f328173f352f3dfdca0ff5e3185f26de77c6f75": { + "balance": "10722917874680000000000" + }, + "4f47a62aba6ea049fc0e92d8afbe1682472d98bf": { + "balance": "10000000000000000000000" + }, + "4f4c3e89f1474fe0f20125fae97db0054e9e14e0": { + "balance": "50203638983000000000" + }, + "4f5ac8dfe79c366010eb340c6135fbee56f781d8": { + "balance": "50000000000000000000000" + }, + "4f672cbd373183d77d8bd791096c6ebb82fa9a2a": { + "balance": "978111227765000000000" + }, + "4fb179c9c88decaa9b21d8fc165889b8b5c56706": { + "balance": "24750205150000000000" + }, + "4fbcf391c765b244b321875d6ab4381c44d0747a": { + "balance": "99999580000000000000" + }, + "4fc979b38b981fca67dfa96c6a38a17816d00013": { + "balance": "1088876196468000000000" + }, + "4fdfdd1832b114b4404aae23305c346beee14e1d": { + "balance": "278724179057000000000" + }, + "4feffb1836029cd0e9b8f4aa94b35ae3982fa770": { + "balance": "1674590934924000000000" + }, + "50045745a859f8fce8a2becf2c2b883b3723b2c8": { + "balance": "169000000000000000000" + }, + "5028bde29fe88e03e3de069b3907fa9df551c379": { + "balance": "196000000000000000000" + }, + "507096ed771fa8a1d004ee5377c01506df461b32": { + "balance": "2669205000000000" + }, + "50788574a0967580fdaddc4758f834d8978455f6": { + "balance": "1648581593000000000" + }, + "508d8e8f338ca98d3c09f0f15fd9e7baa80701e8": { + "balance": "16000000000000000000" + }, + "50a4dc916845172b83764a6c8b4b00d6d02d41d3": { + "balance": "3020744393592000000000" + }, + "50da06418780c220ced4898af0b1fca533f73cca": { + "balance": "36486700700823000000000" + }, + "50fb6fd8432a66219e754234e9eea1dabcc07676": { + "balance": "489500000000000000" + }, + "5104bb1b831902333732dd25209afee810dfb4fe": { + "balance": "1333614132000000000" + }, + "513963743ec6ec9dc91abf356b807ebad64df221": { + "balance": "1508002412172000000000" + }, + "51397ca69d36e515a58882a04266179843727304": { + "balance": "941648956414000000000" + }, + "514a58f2b36c2cf1b6293c36360cf658d8af30ed": { + "balance": "1233397704089000000000" + }, + "514fe0cdb3de692cab9f2ef2fd774244df71be66": { + "balance": "9670444445882000000000" + }, + "51583128081fd800d9550144afebdf3fe88149cb": { + "balance": "231190355520000000000" + }, + "517384fe92391187d0e65747a17bfaadf967c331": { + "balance": "1943121865489000000000" + }, + "51aebfaa26a54071cfe6c2d8f81157ec313984ad": { + "balance": "1422225031261000000000" + }, + "51d4f1205b272e491e94fe21f0341465f14141fc": { + "balance": "552384783614000000000" + }, + "51de598faa85276bb26a68b135028755304b6700": { + "balance": "2068484560002000000000" + }, + "51e08e0304f08ef768c80ca149da4721fcf482b0": { + "balance": "194629207228000000000" + }, + "51fa3da695e24f602952a71966f37ac3596a94a4": { + "balance": "17008166261720000000000" + }, + "520b22776b1befd3064636da0dd251afe569ef13": { + "balance": "18538137781909000000000" + }, + "52219a1e1aa82b78b971088c30583a3bbe675c8e": { + "balance": "411959222637000000000" + }, + "5252b8a0688096523498cb5c1f42bcd1f61923d7": { + "balance": "1863936864000000000" + }, + "5259154e1a5a809b2e3dab80372124cebbfd56e2": { + "balance": "110000000000000" + }, + "5264f2de516835e549710bfe34ef03b08b8557dd": { + "balance": "1216000000000000000000" + }, + "52b17fae7e9cac447f026db71dba4034a1d53174": { + "balance": "99001631977000000000" + }, + "52b3363ae882a99354faeb76733d0fa2cbb89787": { + "balance": "102517584327000000000" + }, + "52bee7fb24a7fc1f34cf0874ec2f06c5fe847cb1": { + "balance": "54443400591000000000" + }, + "52d1f12d391c7a2f3b52939a61a20da5f85eecc3": { + "balance": "2707175772061000000000" + }, + "52f27099483589e883e7eb789896de39c61e46da": { + "balance": "358944977251000000000" + }, + "52f3b715b678de95d1befb292de14c70f89f5e03": { + "balance": "2989868434000000000" + }, + "53259780569f6dd6753c1da1d53d0b155c5b30d2": { + "balance": "200489122590000000000" + }, + "532e4908e8297c90d75d2280b432b469aaafa2ac": { + "balance": "20000000000000000" + }, + "5334d1e399feacabc9648cebcd93172db95d43be": { + "balance": "25000000000000000000" + }, + "5341665addfb5e367f7a7d35de95b87a0cceb3a9": { + "balance": "60544291695000000000" + }, + "535a39a854ed1c2f0afbc5944f1ee0e2e68cf65a": { + "balance": "2141913781000000000" + }, + "536515c0c08988ee69da1d75f18c706f6b9bf7a3": { + "balance": "169000000000000000000" + }, + "5387a1ce4cd2ef4f90075c15dc3c0744948ec356": { + "balance": "50000000000000000000000" + }, + "539a30ee5724978010990718bb8b0dd25f89fd15": { + "balance": "1306896514000000000" + }, + "53a5f87dfb17149b8c2934a2a9d519ace4ac9724": { + "balance": "4569449510000000000" + }, + "53b24fb36e72c22eb830dc93857a8188b03397a9": { + "balance": "64000000000000000000" + }, + "53cc35b3daf4b8e1982e0e63d0bc68d7252e7fcc": { + "balance": "68213426853658000000000" + }, + "53e1f85147e000ae1ff6a5910407395e388c683c": { + "balance": "20000000000000000000000" + }, + "541f43ff66ed5eb1a1ea0ae3f86355ecff665274": { + "balance": "49562725831000000000" + }, + "5428a31f736c0d2b3c4e80baefb75a76ed44d3f7": { + "balance": "10000000000000000000000" + }, + "542f732aec0873bf531f6941828b6f0ed0611106": { + "balance": "8407722276000000000" + }, + "54300b6a77b95545373b2bba73e60f37c31eb1c6": { + "balance": "1581215621996000000000" + }, + "5434bd65a492a4d14d3b97eb49f6e491350ef73c": { + "balance": "484000000000000000000" + }, + "5444a1735913eeac177d947ef38de7cd6bdfc0a6": { + "balance": "1000000000000000000000000" + }, + "544ffeab53bdc59ef8edaff0042b03c2ea123615": { + "balance": "10000000000000000000000" + }, + "54613713df6c5b89c3012a7835651f25cdac8331": { + "balance": "98684037547000000000" + }, + "5471fb39b4e48c118f855492830ad9e2eaa68179": { + "balance": "91791250228000000000" + }, + "5472591efd048dd60a4d6afdb549e95a65578b0a": { + "balance": "50000000000000000000000" + }, + "547b4c1ae70567fd77a896dc05eb536f502ac8a4": { + "balance": "14037444012000000000" + }, + "547fa9f6f86a2939f9144aacb74e0af60d434535": { + "balance": "428416957729000000000" + }, + "54841d6a478cb9b6e717a9de35577a1a4a504b0d": { + "balance": "144000000000000000000" + }, + "549157e5b1c92a88a0eef335b1bcf4d162482017": { + "balance": "21019502942000000000" + }, + "5492757c55c72ac5946b21514ee16c5065ecde7b": { + "balance": "10446737491000000000" + }, + "54984a41eeaa8e710e4e5b8a7f68c96057b7df3a": { + "balance": "10000000000000000000000" + }, + "549a3717a1bca3f38d24655197c3ccef1e8c273e": { + "balance": "4416133255000000000" + }, + "54b047fbe004191cd02f31163d29bd61ccfaadf7": { + "balance": "52649445905000000000" + }, + "54b125d8b260386633b756056b7d7e78e7071715": { + "balance": "10000000000000000000000" + }, + "54ffad1ae76ab45c4218ced27e49bf2745b2a2e7": { + "balance": "1426474871178000000000" + }, + "550b28968bae36f4e99780c6d7deb54c158be6d8": { + "balance": "10000000000000000000" + }, + "55117923e8393dbf233c0f10819e7de75569962c": { + "balance": "470094520022000000000" + }, + "554a2471e6ecf2320da545d559c40b8b622465ab": { + "balance": "4052895973949000000000" + }, + "55607b39da9480ed8f54d74d0818ab8798136589": { + "balance": "13704276648975000000000" + }, + "5561cbe99fd775f5d0f05903fd62ba4877b3319d": { + "balance": "1007596371374000000000" + }, + "559ba7ab58670d4a0b118bbf6aed7f6fdb276594": { + "balance": "3127762973000000000" + }, + "55b0bc444f2a5952a98f216f61cf07382da1e156": { + "balance": "18683409750727000000000" + }, + "55c0a02dc68123aca7ee0c9cd073ead50b16406e": { + "balance": "99999999580000000000000" + }, + "55c47d593952afd637050c5758a921a204f23fc6": { + "balance": "1615608723958000000000" + }, + "55c6855b3970e5a550f0c75d5727329476406d91": { + "balance": "600705012673000000000" + }, + "55eadbe33899f53138d0fb204f42e272f447cfd6": { + "balance": "1671128311341000000000" + }, + "55fa59fa0fbba06b7184ea78868d438176eb96eb": { + "balance": "1553000000000000000000" + }, + "560a11493b5a0ec28589e80276fe975ee26c6a3e": { + "balance": "10000000000000000000000" + }, + "560fbb31d83bf6dc49e5fb15bd582d70c49fd273": { + "balance": "46015432815000000000" + }, + "5620e17ccf094b1be1a93f6f3388fb96e3a90165": { + "balance": "484000000000000000000" + }, + "5633512298cf74f4d2b8663e6f291e9e25436e7f": { + "balance": "10026444446000000000" + }, + "564423f92b8841b3b1f8bdba443067b580916e65": { + "balance": "465451550122000000000" + }, + "56730e1d11a84970355c43ac7659f2f4786dadcd": { + "balance": "20000000000000000000000" + }, + "5678851984add045f3d054623c198dfd4665d54e": { + "balance": "227651903234000000000" + }, + "569cf18b4bcb99e3f3d27235f2c4c0d8d160af03": { + "balance": "4124979731000000000" + }, + "56ac5f2c3486a9ce744a71599ab89a606e7464a7": { + "balance": "9000000000000000000" + }, + "56bc5936a6ea37c1d0839bf64bcec0d366840ace": { + "balance": "14741201469670000000000" + }, + "56bf62e0135e903525cc46b0a3cce33f4a16880a": { + "balance": "534970476270000000000" + }, + "56da0781a80a0abf5dcda4da35861e9de601bfbb": { + "balance": "166898390441000000000" + }, + "56db15729e52d615a744a04f8a59d63e3b9f735b": { + "balance": "10000000000000000000000" + }, + "56e32ed78e7f5be6b00c28847efe7b3589cdae1a": { + "balance": "1046236086484000000000" + }, + "570f7a08150e0088178276f8116bc4103f885903": { + "balance": "1124393518440000000000" + }, + "57147fdd9b52ef53b4ebd4b5712d29da83f99374": { + "balance": "39000000000000" + }, + "57395fb355fe51f1b32c1baa4e9ee0fc2b8fe05c": { + "balance": "7701013675397000000000" + }, + "5752f0f11ed12bb1d5041b0cee4ddd500cd8806f": { + "balance": "151337200533000000000" + }, + "575907d73ad5ad4980a2037efbd20860afc67ad9": { + "balance": "3568754158000000000000" + }, + "576acb4c0bccc89903ad285ac08c70fde514aaf2": { + "balance": "25000000000000000000" + }, + "5784cb8a17cfb5392c4aeec2edbd173849ca6ee3": { + "balance": "15804767597000000000" + }, + "579234645eb857a3ca51230b3a02b964f8efa2f6": { + "balance": "20576922380000000000" + }, + "57989f9fa52b4c0502e7d0c3caac0c37a0b20516": { + "balance": "462711082812000000000" + }, + "57a55c376ea03c22e21c797d83e2fb039508ad3c": { + "balance": "10000000000000000000" + }, + "57d1612ea1fddacf088b62f625ad8cd49d7517cd": { + "balance": "18001023230648000000000" + }, + "5811590907050746b897efe65fea7b65710e1a2c": { + "balance": "310984892882000000000" + }, + "582ffd8c43966aa8ad3c6cecdfc18eddc56fe5c0": { + "balance": "69136214255000000000" + }, + "583b90b3c4d00b9ddf101efbce75bb811d969fe2": { + "balance": "7839200298177000000000" + }, + "5841fee8b1965141e51b8c146b6af00f6a879a8c": { + "balance": "1210322907244000000000" + }, + "5847a576f7799ba1a35e36906b2c2a5aadeb99b1": { + "balance": "183765768447000000000" + }, + "586dea7ada0a54150f5afcf54198db473ed046a2": { + "balance": "7123598380000000000" + }, + "586f545062ec7dc0ffc213eacd59af80660df570": { + "balance": "10000000000000000000000" + }, + "587187488758f67912bd5bb8a5be787a73d97ee3": { + "balance": "702757402654000000000" + }, + "58be0a3482dc3411571f047f4128387049cb9798": { + "balance": "1000000000000000000" + }, + "58d546e2ae82efc4d8efc887ac6fd30f7eb5dac6": { + "balance": "1486717153455000000000" + }, + "58e7010e6b8d97a556c0e7f0d90151224ebf674e": { + "balance": "20000000000000000000000" + }, + "58f991b3b12d29f09ff4cc2c6e83d576e95b1f59": { + "balance": "25000000000000000000" + }, + "5923a65a796934e69081715657e8dfec8874e40d": { + "balance": "10000000000000000000000" + }, + "593b7c43073b8954355ed76020ff3780dd6ae783": { + "balance": "1403468567787000000000" + }, + "5947f1dbd79a622bcc3fa64b19f9b6eda164dcce": { + "balance": "50000000000000000000" + }, + "596311e2fc09ae1eaee57900f2ca188afd5e68a6": { + "balance": "448723397560091000000000" + }, + "597a3adac4607d457c90817220f67eb4abcf129f": { + "balance": "18000240000000000000" + }, + "598201a9bcff0a773e9323338a8a094e9d9b3999": { + "balance": "74904485722481000000000" + }, + "599e93031704c2ce36308f44d4ff8166e71ae516": { + "balance": "100000000000000000000" + }, + "59af0178699f9f3d8f0ea645dda75356119a6e2e": { + "balance": "152462578058000000000" + }, + "59b0c06e40475cd75728797add9c69c3fdb17b4e": { + "balance": "23147237210000000000" + }, + "59b79577f183b9d39c2b458646a26b2fd6ed806e": { + "balance": "4244859516807000000000" + }, + "5a03b51d67a9c660258ebc030120d5d1d4f687c5": { + "balance": "4451691855300000000000" + }, + "5a0d03dff6754963c757eb15a3339ac6c4ba6196": { + "balance": "215126489934000000000" + }, + "5a34ab3937854e407a8739fa14574d3d20e30d6f": { + "balance": "1375979293937000000000" + }, + "5a352fbeb2fd78bbe0268b0efd34f68d401e2769": { + "balance": "27929247671418000000000" + }, + "5a47c2ca4c0fad7e2fc7bbdf5f2356d68843c564": { + "balance": "3218227936000000000" + }, + "5a538adb2c7f6a80634b0ec20ec5152ff6bb4d5f": { + "balance": "10000000000000000000000" + }, + "5a8fe770c221072a7cba79ae7759cae0185adde7": { + "balance": "11913943233694000000000" + }, + "5aafe1efac688583d7facb09d3e569d58fb5a357": { + "balance": "4713219466825000000000" + }, + "5ab68d762750d5185138187db7751c9f71db5836": { + "balance": "500000000000000000000000" + }, + "5acab69851959dd5a6f0673ef757009ed36dfa3b": { + "balance": "974443209942000000000" + }, + "5ad9f2ab11b5e59b756404395f350aad6019d7a7": { + "balance": "54151179981663000000000" + }, + "5b1dc013ba1a28235cc70e785a00eff8808faef6": { + "balance": "516289257133000000000" + }, + "5b1eeb44ef61c7f35482503b7041162bec9b1e32": { + "balance": "125493885394000000000" + }, + "5b3db31996bca4625d22330686128ec234270206": { + "balance": "362316593128000000000" + }, + "5b401fc9ff3be7cdf5f0df870843bbef94f43285": { + "balance": "1373804724122000000000" + }, + "5b47ba296069041f25768e61be14437b8a469e81": { + "balance": "3152706392234000000000" + }, + "5b5030b5057c0457c190489c5d709d7dbdddee8f": { + "balance": "1154404278000000000" + }, + "5b5a4a782d37154a307868cd79bec9cb2a8f0161": { + "balance": "100277816425153000000000" + }, + "5b5e0b6b7cc27b06456ba4c7816ac4e89e1e26a3": { + "balance": "1023749119000000000" + }, + "5b638e4b6dfdb6928b07586e63d5879dce69a1f8": { + "balance": "1000000000000000000000000" + }, + "5b7be81d6ff5228a2b8c2913deea3f86823f1dee": { + "balance": "36000000000000000000" + }, + "5b7c4804bc2b8c72f3112b73d44b59c0711f83cf": { + "balance": "6803857604000000000" + }, + "5ba26d941544d07100744d8ffd6595a8eb7770bc": { + "balance": "583051897662000000000" + }, + "5bd58fc88733632b63d4f26893bc5c08fb60e2ad": { + "balance": "3480620567502000000000" + }, + "5bd85b5f0ecad08133fceb486c43998e537b3451": { + "balance": "484263880245000000000" + }, + "5c12639a5ab107f9e580cbd2278568dde10758d6": { + "balance": "101293252434000000000" + }, + "5c5522df05d6c6d960394c4762599e74247ab102": { + "balance": "149088856773000000000" + }, + "5c722f3ac94421f95389756af9cd97d0eaa6b696": { + "balance": "1435349483553000000000" + }, + "5c7b14ce51abf629bb0953ee4e2d9d87fc86eb4d": { + "balance": "10000000000000000000000" + }, + "5c8b215403da4e7912c1a1704a949087e091b111": { + "balance": "1440961256910000000000" + }, + "5cab313964f6730888e4158234bbd4806db0286e": { + "balance": "32284637230203000000000" + }, + "5cd736bf65c99469490d0523b10a658178cab10b": { + "balance": "99740204082000000000" + }, + "5ce91ef7ae254b2bd6d910cbf0d380814200811b": { + "balance": "50000000000000000000000" + }, + "5d15fc3a0ba8b3d87b80f9bbf972320112c644f9": { + "balance": "64000000000000000000" + }, + "5d2ccc795b19df400f21f24c0dca4d0e9e898093": { + "balance": "10000000000000000000000" + }, + "5d879b8b31af1e400cf53eb7170f82583190b96f": { + "balance": "93765337844000000000" + }, + "5d8dd54178b68bb36e1963d47d29c123864fd0ef": { + "balance": "20000000000000000000000" + }, + "5da1653bbe8353134edfff6158211ad7ee21dbef": { + "balance": "1491149937915000000000" + }, + "5da733ef41a7bdc0cf7975f83ed24604fbb4d40b": { + "balance": "10343699901151000000000" + }, + "5ddf5d7306f7c603b8d3ff993f03906dca14cd8b": { + "balance": "862558469755000000000" + }, + "5de87ec54e2160c7c2a8eff2d859414737501ae2": { + "balance": "21579321171000000000" + }, + "5df1b805b1361c1f39ca844aebe5ecee8a8d06b2": { + "balance": "411820472746000000000" + }, + "5df86b0a183b5e7f702e4da582ce9a8116a05f61": { + "balance": "256000000000000000000" + }, + "5e22359e20dc14be6930c6c1ce5a0c81c039cac7": { + "balance": "10000000000000000000" + }, + "5e2d38a06f33c784303abf2012f9af12622d9e5a": { + "balance": "10000000000000000000000" + }, + "5e479e616585e7fa84bd6f7465d394a1c0302be7": { + "balance": "10000000000000000000000" + }, + "5e4a55027a0d372f6da042b7f73720b143347d9c": { + "balance": "16175516772000000000" + }, + "5e52e86eda3e05f96e353d7e3f0ee90f08864f84": { + "balance": "21255916842000000000" + }, + "5e91c4d3a21c9dfac2c0994ed8890c78d58626d5": { + "balance": "325349462011000000000" + }, + "5ea797b18caba45d5504e57b80b12f5f5ae630aa": { + "balance": "7805696321000000000" + }, + "5eaec8815e859c34dba88cfe7b7fe28572c964ba": { + "balance": "145852682588000000000" + }, + "5eb974b5716fc4712d431bec7fbb2c49057a7b84": { + "balance": "4890681156035000000000" + }, + "5ee5f8407dedbac839f509419051106219458006": { + "balance": "3042761975468000000000" + }, + "5ef782abb28d1ca889ceb3039eef98713effbf32": { + "balance": "40915083108000000000" + }, + "5f23b88f06430c42570ac3fa33b1c7503b388a3c": { + "balance": "2376070180325000000000" + }, + "5f2b1641c0f2605b090039851aacf297e35632ef": { + "balance": "141615261000000000" + }, + "5f44cc8083340e644d19d3debc84dc14a0cbc53f": { + "balance": "291829106275000000000" + }, + "5f633f89adcc70e9da0b66611a5da108b4b221cd": { + "balance": "50835573000000000" + }, + "5f94ef8e9612b03a5c6ffcf423ada9a19a40818f": { + "balance": "102566595099430000000000" + }, + "5fae1977b76a5e899b384f572e4d94855f9cb52f": { + "balance": "773616125740000000000" + }, + "5fbd22cb3de462c794e523fd1ce36f230cc84b83": { + "balance": "1009995132839000000000" + }, + "5fd91676bc95bd6b5e69db8b9216dc83ed9dddaa": { + "balance": "1000000000000000000" + }, + "5fdda8f5271a08cf1b830faa497019d75fa9d231": { + "balance": "4149626365000000000" + }, + "5fdea351c5eccedf2394fb54437b149ae423ecf3": { + "balance": "100000000000000000000000" + }, + "5fe70ee123cb2e03c768138b2f71c1e1ea75ad17": { + "balance": "1074496282650000000000" + }, + "5fec9df797214459f85a040a559b186ee9161c88": { + "balance": "205282872821268000000000" + }, + "60037df7e4092466656a6b9571437fc4600c66e3": { + "balance": "1000000000000000000000000" + }, + "6009a0bcf531640a5a7f1664a69fe0f64b564ede": { + "balance": "50170000000000000000" + }, + "601668d8b678c95ec5ef98d9d2624decbdd52e9b": { + "balance": "23592727870000000000" + }, + "6027bafcd0ade24fda8c345dcbc812d59df74bf7": { + "balance": "10000000000000000000000" + }, + "6029514f24825c1fadc68cf8614951de5d53268f": { + "balance": "1389262963614000000000" + }, + "606de6db14272a314d778cf0e67913b7fabea45c": { + "balance": "144000000000000000000" + }, + "6074f20675f975ae2c081930cae8f299710f0bba": { + "balance": "10000000000000000000000" + }, + "60850fa9e09d414af3690e4b5daefb1b906b0d20": { + "balance": "10000000000000000000000" + }, + "60ad0b6239dda5df7ac0f0ca941684cf20ae0fd8": { + "balance": "81000000000000000000" + }, + "60d6136e6db631be45fefb9667c3dfa69e9d6054": { + "balance": "651902184266000000000" + }, + "60d733dedec6886908520ba57cab8c9d5c2d7f7a": { + "balance": "555461746642000000000" + }, + "61202238aea4010d115c5c64322ad790576cee43": { + "balance": "10465801848035000000000" + }, + "6142d92b61111657de4b2d65698a3621411e3adc": { + "balance": "100000000000000000000" + }, + "61879bc1a022d9cac8b7d57c8f528065beb10bb2": { + "balance": "72766025231000000000" + }, + "618b15c9a60ad89e7fc28afc79bbf7f28d4998cf": { + "balance": "444855210015000000000" + }, + "61c1169e8ba43ee6b919e5be2eac19542eb913b4": { + "balance": "500000000000000000000000" + }, + "61f1cd6efce17f5458325f022f363fd9772d8f20": { + "balance": "19704989598372000000000" + }, + "61f7d39211a0af2e226d8cbc95fb673168653b0a": { + "balance": "484884476279000000000" + }, + "621aa67f09e6506efb2fd141f080fb1d96693a57": { + "balance": "1694451603196000000000" + }, + "62332fa5127b98bd2a627a0ac22d3a1bdb418efd": { + "balance": "926882233406000000000" + }, + "624a465696ad409586a2e67d84750ba50a971fee": { + "balance": "25000000000000000000" + }, + "624d866f0d61bdefc3ec2210bfe36b6d51018f9c": { + "balance": "199592183194000000000" + }, + "6255d6d3b49443891661b209056d530ecd63bcca": { + "balance": "10000000000000000000000" + }, + "626c484055e6739d46e2ff25190c8b3a4af3fe0f": { + "balance": "1485276462321000000000" + }, + "62865e637d723393ab9654d6439db7fb5abf8803": { + "balance": "10000000000000000000000" + }, + "628a47761d5ce755de88444aaf6d7736b911672f": { + "balance": "18625552918216000000000" + }, + "62df6a38e8b15a1c4f4a7aa7c1736c612f54a0e4": { + "balance": "16468111299582000000000" + }, + "631d7916ddbb5f7c469f8ba07cd48e377560319d": { + "balance": "2493487426430000000000" + }, + "632754f5afcae7dc36d9286cfcd91c14abf0f7bd": { + "balance": "1424933496931000000000" + }, + "635788343997ea9f145c508b0cd2ed36e180f46d": { + "balance": "143040938538000000000" + }, + "636973e7dbda9e3042a8c03e25696d0faf27f025": { + "balance": "5491869128148000000000" + }, + "63707efa26d34d7ceadf4e6439324e7bde0ebc3f": { + "balance": "1000000000000000000" + }, + "637d92494f7872d397340c9b5183dce354c8c43b": { + "balance": "724687404033000000000" + }, + "63b9c2e6762a431752f7669b8bbedae9f37120b3": { + "balance": "1360967549741000000000" + }, + "63bd281d8c4d1279519237a2b68f2a73c228f7e1": { + "balance": "217457311664000000000" + }, + "63c0eb8c9a0019e36ec9a731b4bd947271a5bed0": { + "balance": "36693488147419103230" + }, + "63c6362eff56de328a29b7e9d32ced28f3602b6b": { + "balance": "148335309448000000000" + }, + "63c979c787a7b037693cadfeda738ae33178c009": { + "balance": "81000000000000000000" + }, + "63d4621d91906215d32f6fbcee1ac48bd773f630": { + "balance": "1006939236069000000000" + }, + "63ff99fec1cbd2f6e83c0e6de3c0ea4b7c7e1398": { + "balance": "1201300688980000000000" + }, + "640ffd856e48528b05d5ef1e60348048ce291960": { + "balance": "20000000000000000000000" + }, + "641c25f7c380e2745c81a268384a029b2e2be0cf": { + "balance": "635133477665000000000" + }, + "6427792a164bbeab45f6c3acf17c76f721b90e81": { + "balance": "10000000000000000000000" + }, + "6437986b4c545af9c4a5ee96371a5807275e9221": { + "balance": "2951152516627000000000" + }, + "64460d09d1bc5c425d62bef5969eb0c5916963c3": { + "balance": "1680000000000000000" + }, + "646381f92216b97abbd86ca100a773eebdf7545b": { + "balance": "211234535515000000000" + }, + "649f73d1cafeb3ab0631432f04c9d08b9f438c22": { + "balance": "248900746448000000000" + }, + "64a239be45a92df83bb85b25f8ed7de5d82313b9": { + "balance": "100000000000000000000000" + }, + "64a3d97f82e3d42eea78bbcee31a95d33767b055": { + "balance": "2511466286000000000" + }, + "64ad579975888f455217e0f801e371900d9814c9": { + "balance": "7118859416319000000000" + }, + "64af5edbfec8adea679951662c08a781175688bb": { + "balance": "822966999709000000000" + }, + "64b7f2c22c20a59c07cb0dd7f8f692153c68f3f8": { + "balance": "20000000000000000000000" + }, + "64bc17e28d468b7b8368ee8a8375710d21c3ac5d": { + "balance": "875002262415000000000" + }, + "64d17aa662e56061cebb3c2e2421e637163e8dd3": { + "balance": "363241251465000000000" + }, + "64d714ec3145308e8f939bab7591b0773038b886": { + "balance": "338231954012000000000" + }, + "65199fc9ba95434382c108b44ac553534a9a3670": { + "balance": "2537340957145000000000" + }, + "6527c67c29e47833dc2440570596023318a7bd99": { + "balance": "555434226832000000000" + }, + "654b9d299077c90768c5ca6635e5802e8099f51a": { + "balance": "119004827465000000000" + }, + "655908513607cc38de35351ff3738b201bbf39d4": { + "balance": "652902936029000000000" + }, + "656ad16063b2d397788c231e537384ece94eb0d2": { + "balance": "63116382606000000000" + }, + "656e622970b8829a7cfe24f5b82696c7777683ba": { + "balance": "20390269890405000000000" + }, + "6583a6ff4dfcf447e3b163a61b0d5cb84ceee375": { + "balance": "3858529344000000000" + }, + "658d2b7e8a6517256efafd74321757d5c384a2b9": { + "balance": "221114751567000000000" + }, + "65920758857ee5b27b0f31487ccc3c5d6986df3a": { + "balance": "16272975796000000000" + }, + "659d60d67a07774ecc5cfea9e56809bec024d639": { + "balance": "20000000000000000000000" + }, + "65a1a3f968bab5fc1f097b8e297099a3d34ef45a": { + "balance": "16000000000000000000" + }, + "65b5e3163d20b2a6fc75c0219b7f97d83479a26d": { + "balance": "1716459529041000000000" + }, + "65c9bc3b8b0ce7c4d16b35abe1a5c285a59f672e": { + "balance": "20000000000000000000000" + }, + "65d5b458d9b1a9659c1125d20d970d5e6c29dc3e": { + "balance": "20000000000000000000000" + }, + "65e75bb8ade25eb7975ea12b9afdb17ac21063b3": { + "balance": "2270407774714000000000" + }, + "65ed78d0c4ef1150e8765b24b210f056e079cd59": { + "balance": "500000000000000000000000" + }, + "664ee5e334b8378928becfbf5d5e51daaf001125": { + "balance": "860160259186000000000" + }, + "6679bdb26adc179d046607d49f4b10c65d8a40d1": { + "balance": "436794739763000000000" + }, + "6680fe9d6eda3ab9fc4ac1ac933339b533eb682b": { + "balance": "551296206326000000000" + }, + "66a1249501cc5076b040bbb165ce032ace216ea2": { + "balance": "36000000000000000000" + }, + "66a475d014c2f976704bfb93ce78dbabbfc5e072": { + "balance": "1140135640169000000000" + }, + "66ae43d92e8fb2231fee8c72d720ff90cdd267ff": { + "balance": "796696150339000000000" + }, + "66b7e0c810d6959afa8210f6ca67e3e40bd24eb9": { + "balance": "16000000000000000000" + }, + "66bf8be16f33b111b2a425743bb7ebcdfbb35034": { + "balance": "538590591000000000" + }, + "66d2eaf7fe10900d93eab17823ebfde5486aa2b7": { + "balance": "121000000000000000000" + }, + "66e525bb01b3ede1a4a105bb6087ec8a76200616": { + "balance": "1506610219207000000000" + }, + "67291e0df83d6e9f1386e87a1792d7d147341df9": { + "balance": "272330177662000000000" + }, + "6730b27b62e064b9d63df3bcbb8c4bbb0e500afe": { + "balance": "331282968154000000000" + }, + "67318617bfe19b739fac9a126fd129223db52498": { + "balance": "12699924981000000000" + }, + "674dd0b036c91f3a83288af44897b4ceb2e15a12": { + "balance": "4352791270187000000000" + }, + "6751bffd04be55c86692994fed06694cb78b62ff": { + "balance": "26049487516000000000" + }, + "6768d99a0cdcd7bb7c7d0aeee466d6bdc7208bbc": { + "balance": "309909685000000000000" + }, + "677ba2de3e5c68a4c354c9e3129ed1c41025312b": { + "balance": "127426274611000000000" + }, + "67b83745856551f1878027843be20e1473191944": { + "balance": "185757248875000000000" + }, + "68170edcfaf2c6df4e6542b2856ad33e9e2d6623": { + "balance": "4003453949471000000000" + }, + "684ae403d9a08e4f4f971cfedf81094074daa77f": { + "balance": "25139713925794000000000" + }, + "684f3b8a749c002aa434bad6af7a3e2579c69315": { + "balance": "16000000000000000000" + }, + "68538a9e8246be5a5c5ea315cb325344062cf8c4": { + "balance": "14009193210480000000000" + }, + "68935ff3a3a3b6ef16ae7df58cee50b157658dd2": { + "balance": "20000000000000000000000" + }, + "689f508256ea64f5dbd6bb77f1ce1bdaf36d7152": { + "balance": "10000000000000000000000" + }, + "68a3e6e7c191a8c1add988bfbbb9b51d4f36f521": { + "balance": "10000000000000000000000" + }, + "68a74ff2a5577321f854b56d3834a55d3c41bd94": { + "balance": "88873831171000000000" + }, + "68e6da521bde13cf4e4f423a78fda2f69b3d1c2a": { + "balance": "538392460838000000000" + }, + "68ecd5cf8cf8d9704fafc36d8da53930afeb0553": { + "balance": "1090923641219767000000000" + }, + "68fd0b8e000bd2788be6cb10fc0496fe2cbe155d": { + "balance": "32853847745000000000" + }, + "6904045feb5ef94e096894b863d314ff8a0f206b": { + "balance": "9892165615000000000" + }, + "690fbae5153849bb20797af7b8dea66a728a06c3": { + "balance": "6082107223716000000000" + }, + "693d909842877d017e0f102e37a55024517dd0ae": { + "balance": "20000000000000000000000" + }, + "694cd00fac9cded484ef2cfcd44faf161354f288": { + "balance": "3049716150137000000000" + }, + "6964c3c2c7bc719ec94a51bc4bf412e137d2b4e9": { + "balance": "1000000000000000000000000" + }, + "69a5c692516940bebad8efaa2243a8fbdf2ade62": { + "balance": "2803346939929000000000" + }, + "69f566c44802b0140f5e1c9234f46006773c03d4": { + "balance": "20000000000000000000000" + }, + "6a17eef3a6bd407260f52067592226448182cdc3": { + "balance": "1116509364305000000000" + }, + "6a200e99a0f50aab32fa7373c7880817c81f472a": { + "balance": "1836680122795000000000" + }, + "6a2a29f5f441876816dd17856051040787f48a64": { + "balance": "1131603204000000000" + }, + "6a3f855c7dceb75d0de7fa18fbc2f40c81b76756": { + "balance": "32267494586000000000" + }, + "6a46af653b938643e781cc4a0edcf5357852fd21": { + "balance": "1140718780752000000000" + }, + "6a4b2e5b45da0d70621ce71f165a11078a1745e2": { + "balance": "3768326643000000000" + }, + "6a530c813595a5b7776cced05a865dedcb110d94": { + "balance": "270559347097000000000" + }, + "6a6e3e82f98ce891f47721770301dbe2652a9e25": { + "balance": "10000000000000000000000" + }, + "6a828d6f2f7f68bde4a12608024020e593540010": { + "balance": "7531817000000000" + }, + "6aaddd1f4ff6b4d414c87271619b826ead27f09f": { + "balance": "64000000000000000000" + }, + "6ae6bce1e2865ade0d02eff9899ea3767b5511cd": { + "balance": "6893781798524000000000" + }, + "6b04e7c6a837d218fd3322b87a267fdd979358ef": { + "balance": "302679180175000000000" + }, + "6b2210b8536803b134e69c5046904acafef48cdd": { + "balance": "47823456459000000000" + }, + "6b2da6f36c2e7f61cabd7580480065360c995c93": { + "balance": "55000000000000000000000000" + }, + "6b3401986f2be7ae5a4ec160b8f96b2a651fce73": { + "balance": "16000000000000000000" + }, + "6b3847774e99dec307dcf5bf5adba49df4a9f145": { + "balance": "43276069579000000000" + }, + "6b57f2d9d95cac67fd2f70c0911d48c7f09de072": { + "balance": "1000000000000000000" + }, + "6b65d736a8ca89ec8508b52e4aca5166f9703732": { + "balance": "766421968820000000000" + }, + "6bcc55d897829e98fc3f3ac8beb331e59c33b942": { + "balance": "318115956882000000000" + }, + "6bd76e7af1775b88743d5f53ede0ce846d3d7ced": { + "balance": "139548017482371000000000" + }, + "6bd7cca99acf6eed5842417c2327c642df5473fd": { + "balance": "3321731000000000" + }, + "6bf72c4d39d6700181954a8d386c3df216634412": { + "balance": "12742769034078000000000" + }, + "6bfd3aedeac7c6ec086c0a4ec29d2d0f5bd69bc5": { + "balance": "50000000000000000000000" + }, + "6c025962810a6fb8374af5e07d7fcd631d10b1ce": { + "balance": "674126722005000000000" + }, + "6c1b72df836f410038af9e020fa2ff2ead398ef4": { + "balance": "1851293017364000000000" + }, + "6c1fddb4254ff46b3750de322ebb7d6238c0a606": { + "balance": "9977629348276000000000" + }, + "6c37069a361c5c72355bb5a56879dd0a9735a237": { + "balance": "1062230154063000000000" + }, + "6cb166eeca248a234c971b2a864a7b3fdbe5a737": { + "balance": "390222992865000000000" + }, + "6cb797289059cadcfa77eab0365e6bf1ae12df46": { + "balance": "100000000000000000000" + }, + "6cc787e6bb4f484828b080330667b93953e7a3c9": { + "balance": "16106440380234000000000" + }, + "6cdf7b334fb2ef8115198d475d431eeb7d88df77": { + "balance": "1940904395351000000000" + }, + "6ced85b035b787e9e427d0904aaf96e011417310": { + "balance": "103417697874000000000" + }, + "6d6e09acc07f388cbab99e53959f75e9ad8f07bc": { + "balance": "1305917678000000000" + }, + "6da91b02f512f412d374392247a9aaa853e9dd59": { + "balance": "2300525907893000000000" + }, + "6de5d70481cd40db468f64227228cdd362ad9980": { + "balance": "10447389944082000000000" + }, + "6dea87255c9ebfa63f017209046e894ecbbc03b7": { + "balance": "1527216854064000000000" + }, + "6df6f6b9953c2f2a8ce5985e19dd6835ae2c566c": { + "balance": "6539856530000000000" + }, + "6e013c83cac111a38fbbf8d47778fda0d3af25d5": { + "balance": "12139181929380000000000" + }, + "6e18a484f402fd433a5ac4dee5a4b8bf6f22db47": { + "balance": "23215906572368000000000" + }, + "6e4fd058e4dcd502c2015f83f3677f680ec58110": { + "balance": "480059342014000000000" + }, + "6e501ac7357fc758caf5dff6c29a995c806a1a7f": { + "balance": "1573491311733000000000" + }, + "6e6912f9fc21dfba736055e6ccef074dd62dcc59": { + "balance": "256000000000000000000" + }, + "6e869c68511c1458f4fbed9a4c5296fe961eb47e": { + "balance": "68488423994541000000000" + }, + "6ea6827b377b3d3ecf7c7628ed8daad7fd8eab1e": { + "balance": "188825714738000000000" + }, + "6eb9237738339fcaad3763466509f23efd0c5054": { + "balance": "48417242786000000000" + }, + "6eb92a61390f9d9ecdac80a8833aa801c3926b13": { + "balance": "1412936326723000000000" + }, + "6ecb93f18153ef2d2a552286ea3b7436f1f8168c": { + "balance": "20272577229669000000000" + }, + "6ee087c04cf16f4768c783a548686448fd125914": { + "balance": "1397039628538000000000" + }, + "6efbae7a34c71233329d0bb4cbec45274824ebf4": { + "balance": "8910000000000000000" + }, + "6efcd6776f287c25a6eb3cf71018adc282eeab6d": { + "balance": "1310659853178000000000" + }, + "6f9ca805ddaaea5205e85778dedb2eff4a5aaa75": { + "balance": "2585733757016000000000" + }, + "6fbbea927469f4d18942ce0aade164828fe23a2a": { + "balance": "4671857880000000000" + }, + "6fbe9df6c42151c453502960d99170445dd3ac0a": { + "balance": "20060296562115000000000" + }, + "6fed121fb310431f1659e637f35f4c878a7256c7": { + "balance": "55170085399000000000" + }, + "6ff2dd5373bd72966ef48d3183c60d74a6549cb9": { + "balance": "24103445361000000000" + }, + "703a490c4783776da244384c964897491aed3711": { + "balance": "2001677632732000000000" + }, + "704dcd2d9f75f0bbfb73f2fe58bcbf4508374381": { + "balance": "439603954369000000000" + }, + "70859a14f33b8ab873fa5781a4af1ce40dff65c0": { + "balance": "10000000000000000000000" + }, + "70b9cdfa5f6d41c60e1c0d3f544f569c9b340ea2": { + "balance": "198355566698000000000" + }, + "70d0ee793e28e320b34267ef2df69050fca0a9e0": { + "balance": "8010660534227000000000" + }, + "70dc7e5951752c22a0e3c50e8e7b1f7af4971d51": { + "balance": "3991137321749000000000" + }, + "71057f5afbed7d82c92d50790e3797fd7395d036": { + "balance": "49000000000000000000" + }, + "7109a3b3d5d6af49693549728691099d696ce016": { + "balance": "4119694297000000000" + }, + "712231a5161745fa1b33c7b0f6e8c767e1de4f81": { + "balance": "1353809351914000000000" + }, + "712aa38999c0be211654e5c84f59e3b2e018f597": { + "balance": "160199774000000000000" + }, + "713229fc94a86b71a5bd1ea6498b9373e3f3c549": { + "balance": "98289185940000000000" + }, + "715de29a0b6f467b94d4a90dc767ad52d0fb3b9e": { + "balance": "948824982990000000000" + }, + "71776853ac97ce04b008c9a7b64156a3cafc52a4": { + "balance": "608309596513759000000000" + }, + "7189f6dcfe64e1ddbfb5e51fd5f3174bc636dd0e": { + "balance": "5674608906899000000000" + }, + "718a4da87464caf6e83ca374d5ef9255b8f7cc3e": { + "balance": "761891873568000000000" + }, + "71bc447761cdb68915cc2288b4929fdc0adce02d": { + "balance": "10000000000000000000" + }, + "71d78531896642069b725bf82fc385789c63217c": { + "balance": "33103960195000000000" + }, + "71e328deeafbb1724051d1062609c43eef56ecdf": { + "balance": "493550967964000000000" + }, + "71ed0310fb51b86a61794aea17a3c792dd301e3c": { + "balance": "3234918634449000000000" + }, + "71fa264f58041e41cfe36e8f8d4e0cb22ab71925": { + "balance": "5558941960000000000" + }, + "72059c57d0fc05bc02ba54ebea6cefd1efbeadf1": { + "balance": "4458278271443000000000" + }, + "720847a28916a532bcab33e1fcbde5d1c4d820bc": { + "balance": "1392418942284000000000" + }, + "723cd2b5b836b0ee8481d37b9c51b5f3f1beddd2": { + "balance": "1856420455522000000000" + }, + "72430c6664d23c7051b0e99912fa54dfadcfdeff": { + "balance": "102078926010505000000000" + }, + "72652c4320dda25348f15c0ecfeb4b3b3ceeb7c8": { + "balance": "307639955659000000000" + }, + "7288bd1b9f4c068dd5df9bcd6fec1ccecd240195": { + "balance": "80161087899000000000" + }, + "7299cb8a288abe8e1a22c11b53a903acb7db5827": { + "balance": "752198565719000000000" + }, + "72f6bc0c3ae437756c099e02e9c084febedc5569": { + "balance": "696294297587000000000" + }, + "730e5907b344c80e0a6115723a90a23e3635192f": { + "balance": "6056082041729000000000" + }, + "732e97b992e4f8a53034cf29cf11aacba7452261": { + "balance": "100000000000000000000000" + }, + "7339df65ce293b3d501647a04c83819099f0bd38": { + "balance": "706500983417000000000" + }, + "73482f8135ca2231db5e0e034a235a9d244a8656": { + "balance": "1143989148865000000000" + }, + "73769e43058d30a530048e5a2bea7e9333534e93": { + "balance": "113542901996000000000" + }, + "73bb9e6f1709fbb7964df7b3cc0f9170c3152f38": { + "balance": "1639793026701000000000" + }, + "73e261da7978764044ee916f88bf66680952607f": { + "balance": "100000000000000000000" + }, + "740154120c4f41c50b0aaa0636a2000ff1e870ad": { + "balance": "10000000000000000000000" + }, + "741fe2a1537284b70e97e3ff659eedfd7fc5b1b6": { + "balance": "75911502037000000000" + }, + "7420bb277d834763e4429db9bf37f053f71ab769": { + "balance": "3100160195046000000000" + }, + "74281371c3b569c774da6bab686e7d7a45d4dc4c": { + "balance": "25666397941223000000000" + }, + "7428d261b5418652c5ab248d6abc3d2af25d904a": { + "balance": "56252809397000000000" + }, + "742c876433297f5a8fd4a25f75ee9a607726bd3c": { + "balance": "4132793019677000000000" + }, + "74302036cf52e11aa3f32a371bb4992e2bdc3f39": { + "balance": "19557661364000000000" + }, + "7445c657c24d014f3a9dddc3e446868bc2dbd13e": { + "balance": "10000000000000000000000" + }, + "744b8fa69d2542be3557267edaeaf2cfa8a9e991": { + "balance": "16000000000000000000" + }, + "74728999963524e7cc1736abcb4deac630142c44": { + "balance": "37000250991000000000" + }, + "74926cbdacd0e871cad0d926c8e17cb2c00475b9": { + "balance": "20000000000000000000000" + }, + "749e115a9e675bb15af5e1c04f81fede07c40120": { + "balance": "440913547154000000000" + }, + "74b7e01acf825898544d6c1b61e53356be759c56": { + "balance": "25000000000000000000" + }, + "74c5fcf875e2e9b726a7cf6e176dc2f7eb84c200": { + "balance": "59208835472000000000" + }, + "74f44579859e4a7944dda7bd810088e116ae9910": { + "balance": "1038454108527000000000" + }, + "750b1e2955ba05c1fc8a1f9dbb1624ed11587edd": { + "balance": "9545712605000000000" + }, + "75375129cff2a051f656b91f868325c3b35ee1ae": { + "balance": "25000000000000000000" + }, + "753ca28fbd89081382a996fe938da7e6c3ae6cfd": { + "balance": "156582454263000000000" + }, + "753d91c04e554680cc32a97c1abc96280e8263ee": { + "balance": "725101425969000000000" + }, + "754e5b5d64c267e83fd4804d112725531cf5abe9": { + "balance": "83276113115000000000" + }, + "7588a96a2bc65569a6c124c4a4acc55863a8ab78": { + "balance": "24062602342000000000" + }, + "759075dc3a6b9d2499a74bc57e346c9ed7ff834e": { + "balance": "225000000000000000000" + }, + "7591d6fa043801fe12462e11d9e33a53f438c073": { + "balance": "1863874274000000000" + }, + "75bda5bdf6aa749bbd62b6107941a7dd9ce3880a": { + "balance": "36000000000000000000" + }, + "75c2d3a99f144c4b9962b49be9d0a81b203906e8": { + "balance": "9000000000000000000" + }, + "75f587a69a97eb4d1c4c4078418d5fa85dff6f94": { + "balance": "10000000000000000000000" + }, + "75f67649605f49d98d866102ea2d6881ead9bea0": { + "balance": "814929108418000000000" + }, + "7602abce0f510b6ca471fd8d734e21a2591886f6": { + "balance": "50000000001006000000000" + }, + "7629b788160531b0be28bf445bf305fbe2c514d2": { + "balance": "23022256366212000000000" + }, + "762aed2e3aa2293e69dc2110b1fc6c806ae799a5": { + "balance": "10000000000000000000000" + }, + "7637b89130bc3f87e90c618fd02d6dd27179101d": { + "balance": "77765738300000000000" + }, + "765136022facade53e7a95c0c7aa510787e674d5": { + "balance": "1478178932688000000000" + }, + "765274015a308a9e6b1f264e5bac592d267f2f7b": { + "balance": "3058788819393000000000" + }, + "765cbc0a89fd727a2c1a6b055139faee53f11330": { + "balance": "500000000000000000000000" + }, + "768bb6d4b190c18a0946d92073ee446d68d98a6f": { + "balance": "144000000000000000000" + }, + "76ae8079894c760f2850c02cf5a0d7bb41e5864d": { + "balance": "156059816821000000000" + }, + "76af4103a231b1302d314c486a0ba524d0427899": { + "balance": "10000000000000000000000" + }, + "76b6394cd02ddf761e981b6a6ce1654c0e575443": { + "balance": "1078304803757000000000" + }, + "76db33eafeaf965dcf15d5460b64a48b37285259": { + "balance": "1000000000000000000" + }, + "76e5721c0a39d41274f84cb572039967a07e9beb": { + "balance": "156298167226000000000" + }, + "76e6ca6ef145d2711ab27f82376a065cc6f62a29": { + "balance": "100000000000000000" + }, + "7705d637cf9f6ceaa452deaca7ccc581beb5fa34": { + "balance": "36254762908065000000000" + }, + "7706c80af4eb372e168501eedfe7bda6dc942243": { + "balance": "50000000000000000000000" + }, + "771493da92c9fc6c6b39a4071ae70d99f6a588d3": { + "balance": "2000677471360000000000" + }, + "7719206286f26144c0f20b5e1c35cf4495271152": { + "balance": "1380480863056000000000" + }, + "771adcba1409fa2df6db19d9f784abc81a7bbf36": { + "balance": "15416381820915000000000" + }, + "772f7baa80a852e05b2fb3903a36061da132b2d8": { + "balance": "121000000000000000000" + }, + "7731a4175eee5077e2ede48878e6e2a18fce0f9e": { + "balance": "10000000000000000000000" + }, + "77385deeba01e3cd7a63e13d6048011020f56724": { + "balance": "57204247488000000000" + }, + "776808e7688432755b9e91a838410d29e532c624": { + "balance": "120318608715941000000000" + }, + "776d1b406f63082b80e250c4a0073fa0d83b9090": { + "balance": "243779839900000000000" + }, + "779848a59036ee3cd23b93ff6d53620d874f0bee": { + "balance": "82228810849000000000" + }, + "77d02a031274bd4ed2a16f3cc29d94e755142036": { + "balance": "408567696646000000000" + }, + "77d609a407aa0d126d58090b8d635f5ab7a02d6d": { + "balance": "776754055755000000000" + }, + "77dec41e116301dbd6e542f139816bfd9bf6d154": { + "balance": "16335989583000000000" + }, + "780398b42f81167731a8ef6a8bd1d14942b83267": { + "balance": "25000000000000000000" + }, + "780a645d59027e7b0670d9565898dc00704cbe5f": { + "balance": "20000000000000000000000" + }, + "78182a7711c773f306ec42ce6da3e983cd49b00b": { + "balance": "580861257254000000000" + }, + "7822622f07fec12995c4bb8eb32d62aa7f00be05": { + "balance": "5018461926846000000000" + }, + "786410c679101c0ebf06fb4f36102368121f3c8b": { + "balance": "16098386724761000000000" + }, + "787d5476038ab0a09b846645285ada23ffd7318c": { + "balance": "492047430907000000000" + }, + "788e9e27ed979d1e7aefadda798f69df1de1d1bd": { + "balance": "30965301214000000000" + }, + "78ab2d2dfaf5d2580ed89c970e771572bc91d3be": { + "balance": "36000000000000000000" + }, + "78ab7ac6f379ff084a7acf4a1a31fe2e5a6834c0": { + "balance": "107332516726000000000" + }, + "78aba95da37385c736ef93d0ca8318baf6c5ff3e": { + "balance": "9000000000000000000" + }, + "78cecbd82229dc91a530bd555c9e45125e2a6bc7": { + "balance": "28474069251604000000000" + }, + "78d4df90990248f3ac67e492a0a1e3f4ee455507": { + "balance": "10000000000000000000" + }, + "78f6de3768abc604c49b10d798e0656948cd334e": { + "balance": "9000000000000000000" + }, + "7909aca95ed899743de222e56c231f9bed1b518a": { + "balance": "5355599376491000000000" + }, + "79193e660b4431e8aca9c821b7daa88064e33750": { + "balance": "100000000000000000000000" + }, + "792487caa23b0d9b9998002810cf29439f7190bb": { + "balance": "4828579961131000000000" + }, + "793f56adea51063243a9633ecc1d1e620a91f327": { + "balance": "926742377449000000000" + }, + "796d187077c1d7591583436ae64d10a641490ca5": { + "balance": "242664407084091000000000" + }, + "79a6b7fad3b5a655679450ca82818ec2d6f58688": { + "balance": "1400472715109000000000" + }, + "79acf627e67cedf48297c26fd135973bff6c57da": { + "balance": "444598475759000000000" + }, + "79ae0dda1964ff0191b98d28c9b52a79dc9ab078": { + "balance": "325908985422000000000" + }, + "79e71dcc52fa1b28226c519f715faa3cf63cfb09": { + "balance": "497898493594000000000" + }, + "79e98193ff8770f26af824734bbb1c2ce8197b6f": { + "balance": "10000000000000000000000" + }, + "79ff3d790d52c58b7317a415278e9058915d5241": { + "balance": "48502649691864000000000" + }, + "7a0b02d16d26e8f31e57106bbdad308f513d436c": { + "balance": "841000000000000000000" + }, + "7a1d422352ec7e6ca46131728e4b71f20ed84e2f": { + "balance": "50496873413000000000" + }, + "7a2a3fbe27e33df867ba8800788995d7662c046b": { + "balance": "100000000000000000000000" + }, + "7a629c4783079cd55633661d2b02e6706b45cf8e": { + "balance": "50000000000000000000000" + }, + "7a62d8875f53e54b775ee2f67f7e2ec137bf724f": { + "balance": "25000000000000000000" + }, + "7a67285fd883d36ea3107aa3fe7727c68a99eb2d": { + "balance": "254787158217000000000" + }, + "7a90fbec48492473d54b0fad128ceda94ea66100": { + "balance": "313715004199000000000" + }, + "7a9e11463d84a08140d698972e32e66bacf7a7c9": { + "balance": "3602603216258000000000" + }, + "7ac4f33e1b93ef0f9c15014e06da24904ef4419e": { + "balance": "101000000000000000" + }, + "7ae082ad247275fd5a9e77b127cee5693784e9e1": { + "balance": "1921957343533000000000" + }, + "7b27e070ca4158d13f8333b34842d4c28b678c92": { + "balance": "10000000000000000000000" + }, + "7b2e34374921e4dc10fd9cfc670a40f5d092da1b": { + "balance": "2098457950503000000000" + }, + "7b54c6c8041c8b09240de1ff06e0d3d2d8d877e0": { + "balance": "944752036841000000000" + }, + "7b5aecb798d8f4f5a04bdaef909e09a35bde8d47": { + "balance": "21975115049000000000" + }, + "7b88a7ef9201966bd1ca634779c3b7f40c22f0d7": { + "balance": "64344833519732000000000" + }, + "7b8c22ddc5c7e59e571587d7c776fa50e65f4845": { + "balance": "225108110445000000000" + }, + "7bb4d8a169f72432494ac362eeab005ce1e02d81": { + "balance": "2098993419448000000000" + }, + "7bbaaa6690698e749d095447bdd27207c0caee43": { + "balance": "490069993631000000000" + }, + "7bbf27f92f9f726381d4f68b21ed86af8f792d04": { + "balance": "806346082666000000000" + }, + "7bc6f172fd78953c3456c571ac8394756715d5fd": { + "balance": "81000000000000000000" + }, + "7bcca29b477730ee8f219a5d1bca24415c7a4625": { + "balance": "36273885000000000000" + }, + "7bd296e1cb29ad87ed28b0ed18440ee686b157e0": { + "balance": "35964679698000000000" + }, + "7bde6d49a1af34a5a9dac0b9007e9a5583c65ebd": { + "balance": "1041474566346000000000" + }, + "7bea6240f245e649563253fa4c1da39b12625da7": { + "balance": "100000000000000000000" + }, + "7bf096396c56f27f9c39c4056ee6cfcb0db44bc6": { + "balance": "407261849111000000000" + }, + "7c3b58d3ba283bd9b1580832e9d014eff48bff7f": { + "balance": "7074518779349000000000" + }, + "7c5a56c45f23c353ff9f6f71ec86c9a6a1a0ca67": { + "balance": "11277879639596900000000" + }, + "7c783ac9b07bc6576835635f37e7e3c137055c8c": { + "balance": "16253676225000000000" + }, + "7ca2fbc0a0d1370e95048a21a300eac4d6056df3": { + "balance": "2772084065617000000000" + }, + "7cbe95802a20eb765f9fcff0a068859cc35d2660": { + "balance": "255153842674000000000" + }, + "7d004fb3a6a81c00fd2872e8079ad2912841b0e0": { + "balance": "642630220843000000000" + }, + "7d30c788d4ea18849ebae1173373c8915ffd7a35": { + "balance": "61062263242000000000" + }, + "7d39324f5ff62e849b0f0f46ab8ee396fbd85581": { + "balance": "100000000000000000000000" + }, + "7db0ce6c04537417dca1dd3415a5bf213edc2028": { + "balance": "30393443462000000000" + }, + "7dcfaa795586c92f1ce7d5c7b10608fe6a773fe4": { + "balance": "183173395920000000000" + }, + "7ddd111cfdc3133f59b82568e3deefc3cf10b0d0": { + "balance": "5622149283840000000000" + }, + "7de81daaa7ed5cbf4d379cdd26ae353cbd5a2489": { + "balance": "10000000000000000000000" + }, + "7e0a11af993a41626c5564f719442c0dfd608ec5": { + "balance": "1532083534600000000000" + }, + "7e34971b187047e7f7980650630b936eedc11023": { + "balance": "10000000000000000000000" + }, + "7e5214e16851b33c4a4d29e5a06929461d3d9555": { + "balance": "371790231197000000000" + }, + "7e52ae9c7e4b888015a3a5af7a91444510aa18e2": { + "balance": "109879329128000000000" + }, + "7e69b383671f96b7abc2d1fed8b61477b87a58dd": { + "balance": "10000000000000000000000" + }, + "7e733b1fcadc9a20dc038fba74e236af0b5a39b3": { + "balance": "43583614302000000000" + }, + "7eadcf955c90040668fb0f75a61f687e4e41f314": { + "balance": "332201682206000000000" + }, + "7eb51f3ead1dd0f5384c199ad5518ec55f77d35c": { + "balance": "38487884822000000000" + }, + "7ee73c0d64caf46f47f439969060092ecafdecd9": { + "balance": "15063618320000000000" + }, + "7ee8e4c6742a4c6d8efbfacc4d56119bc6c74ea4": { + "balance": "31882319329000000000" + }, + "7f16d981521c06347db8324da38b25eab3cee23c": { + "balance": "400000000000000" + }, + "7f6ff7db81a26fe78dd80636f0b178c669344393": { + "balance": "10000000000000000" + }, + "7f792b094c0b96d6819823cf21bc0c402fc27bf9": { + "balance": "50000000000000000000000" + }, + "7f84ae97c21cc45a7e56603ddf97449d803fb246": { + "balance": "81000000000000000000" + }, + "7f89c2b9daba034841f19ae843cfb6cd6f75b1d7": { + "balance": "20000000000000000000000" + }, + "7fb18f8b0e1fd1ed8c863a66226082bdc0429ee6": { + "balance": "11465417544634000000000" + }, + "7fb4e30579c64efe981d0057204e5bd8770a1f87": { + "balance": "249801873762000000000" + }, + "7fcc4de10e837d98691acc52732e1568c890304a": { + "balance": "1000000000000000000" + }, + "7fcc77798cd50345b2784a78b81a25dd4c1e64ab": { + "balance": "2676882485895000000000" + }, + "7fe33e773a02b995278ff595d55a0741813b19d4": { + "balance": "5788279057355000000000" + }, + "7ff32b13d531ceef500ca6c6806ffc0773639264": { + "balance": "1000000000000000" + }, + "801380158ef8f24316bdceaa00eb89c3d886707e": { + "balance": "35627521347898000000000" + }, + "804fdccdc8603858d15dec88666437505b2a106a": { + "balance": "14607090269617000000000" + }, + "807915567eed99bb9146354a32409812b9490d70": { + "balance": "1083142734057000000000" + }, + "8092ceeb2be5b271f4c156d85fe14977e919c7e0": { + "balance": "761607160308000000000" + }, + "80962bf961d0d713395dbe00379a6e207b425a76": { + "balance": "524215754483000000000" + }, + "80a9787124075c8cd44b9c8674967a54445e2354": { + "balance": "7600078997429000000000" + }, + "80aacd59dd76bf443c47ca02976178af8453f23a": { + "balance": "411856023767000000000" + }, + "80db788f7fbd7613f0fff66c21389eedbbd4bd35": { + "balance": "956888725645000000000" + }, + "80e449a70e3c7707d6441ae8863a44aee2d7f3f2": { + "balance": "16260784762856000000000" + }, + "811a2c3d0ba4e1c36a848495585da824ec3a7620": { + "balance": "36000000000000000000" + }, + "812a3c55234d5849a854ad76891c34ee90c8a0e3": { + "balance": "703378980438000000000" + }, + "814b4b5eb67afb8d1a60e3d240fe804bb752f632": { + "balance": "17578964576000000000" + }, + "817025619f37838470b90d0a25af2c02de80dae6": { + "balance": "96000000000000000000" + }, + "817233a104d87cac34d9c90243aebd7f68e0a9ea": { + "balance": "510051038684000000000" + }, + "818be95c0c13c3018b4084ea177556705e84c1f5": { + "balance": "332239667000000000" + }, + "819618c19a4a490b821f8156c5633749ea782ca2": { + "balance": "10000000000000000000000" + }, + "81a80d26b70626e07e8747bc1569dd2855834f7c": { + "balance": "521696417321000000000" + }, + "81b2fb0db882bf2538cf8788bae1ad850cef3bab": { + "balance": "102457067052000000000" + }, + "81d4c3bf72837b21203b2a4f90bf42fda10acf48": { + "balance": "10000000000000000000000" + }, + "81df59e5d7b9a2db5463b53be83b4d7c7673d163": { + "balance": "887372337013000000000" + }, + "81ef38d074e0aa9ad618deaab01bcd135301fb67": { + "balance": "24072930558567000000000" + }, + "81f3a4c5291f13f8f97a067a6ed744a686331eaf": { + "balance": "56612148225000000000" + }, + "820610d0ddd3e9f3893f7cc13f32b1ad0d169f81": { + "balance": "50000000000000000000" + }, + "822d6388145e96cdeb2900420a0e0436e940b670": { + "balance": "20000000000000000000000" + }, + "82323b748fdee9f18e34aefc4ddebd4993ac6293": { + "balance": "112752706047881000000000" + }, + "82324995b36f4ff15be3559ccee14742d5b4c75a": { + "balance": "1184047304377000000000" + }, + "8235bfba0bf0fb664271ebe534616456a78852ce": { + "balance": "6804584686000000000" + }, + "824df7b17a61392f88f7e3067f8c261abb48806b": { + "balance": "144857897574000000000" + }, + "82555a7aebfc95a01a3773aa5370394cadef0302": { + "balance": "40069354268401000000000" + }, + "82831d451b8f92fbf6a763adb708010a3e66bb60": { + "balance": "8750983992240000000000" + }, + "8294176178418f46bb18440cc87a07cf40c1669d": { + "balance": "4439783816461000000000" + }, + "82a1c733c3c937ba0a1a49481e4d1f6226157d2a": { + "balance": "50000000000000000000000" + }, + "82ad0b5dc23bc763da0352f5983efceeaee6ea08": { + "balance": "171723633433000000000" + }, + "82b4a3d16655fd71f4020e6a562592a621ff6e1c": { + "balance": "190211621484000000000" + }, + "8357d5a016a00aa5e3ef05d3ce210826adf4c501": { + "balance": "10000000000000000" + }, + "836c41d7f9e72131eff839b7d510fd0ed412f939": { + "balance": "15575572364757000000000" + }, + "8377fff2b0eb03393543ddf5ffae90b3311af5d3": { + "balance": "2058810049054000000000" + }, + "838859e6fd751539a88d00581b0e19bc98c37e47": { + "balance": "338264241636000000000" + }, + "838da0414211392b644e73541e51e9f0fba26615": { + "balance": "20000000000000000000000" + }, + "83958896a43d23ef4ba01bdf6757c36105985096": { + "balance": "9000000000000000000" + }, + "83b88314b606df40d5e716df488980bc64125b46": { + "balance": "10985538717083000000000" + }, + "83bf53fa162e1d85751be0bc6f46e8ec881392e2": { + "balance": "1497107276676000000000" + }, + "83d7c52608b445e18fb1e28dc6198908d66bb6d8": { + "balance": "265446362740000000000" + }, + "83ee8ebaed62092d2116de6b4e90778454e8dfc4": { + "balance": "1000000000000000000000000" + }, + "8402fe573658250f50fbe111596ce35ea9ec01ca": { + "balance": "3479737676000000000" + }, + "8412b877e708a7d5db2a38d9b0f4f23d12231f63": { + "balance": "9225027744855000000000" + }, + "8418dcc09fe052febf2946ee22bcc8c53d548eb6": { + "balance": "3000000000000000" + }, + "84199f54ef96bda5e14f60aa1723e811f755d3bb": { + "balance": "129197612052433000000000" + }, + "841b1400f97ecd2ca008e7b4f5a95274bc3e99dc": { + "balance": "2095180906854000000000" + }, + "844177191a120d2dc4be9169ddbc3b5430e9e238": { + "balance": "3620793599287000000000" + }, + "84578fcffc73be7d65bfa81b0cdafd26885bafbc": { + "balance": "37592478429000000000" + }, + "8460acb05c6c476ca26495aec7224c2bf90996fc": { + "balance": "8999580000000000000" + }, + "84696cdb9f018d3e7bf453efdc174e1a586e9c25": { + "balance": "118007806297016000000000" + }, + "846a8a91d2890000d1e995fc1663cf5b7c22211c": { + "balance": "27266838638307000000000" + }, + "846b5ef52d5f7ccc17d9c7e5f49db807908c63f3": { + "balance": "375423381758000000000" + }, + "847409e5d6ed2c4e54ff97f2ed58217ac5fc3d68": { + "balance": "23972870617025000000000" + }, + "84bf432c967540caafb8bf49cdc9983e8953a18a": { + "balance": "453476687224000000000" + }, + "84eba1bb76f7a3f6d2b9052d068cc6c48d449d76": { + "balance": "17655334922000000000" + }, + "851245ef1637a07578241b3c35acf215908e1898": { + "balance": "1269389304110000000000" + }, + "853708e974fd4810655d9cd19fc8dbfd3d5e1e36": { + "balance": "18000534000000000000" + }, + "8547989af8c99a3432038a03d3fb30a054d90413": { + "balance": "10000000000000000000000" + }, + "854ba39bac4c7bf619804b6773fe43bc71f3255d": { + "balance": "15999580000000000000" + }, + "85636f3e113cbe1d1bbd1b3a23e9e98edbcb94f2": { + "balance": "1199038399611000000000" + }, + "857167896b859394babf897c4c6fa57b3a057117": { + "balance": "921057404898000000000" + }, + "85799226a1474371ca76f05597a1e3835c17e7d7": { + "balance": "562141544946000000000" + }, + "85a2221cbbb47e8b74fc2617d6087a98f47e2738": { + "balance": "10000000000000000000000" + }, + "85be0bd55fb9143ff17387914a82d0a2650224c4": { + "balance": "4038654147145000000000" + }, + "85c5ff0e4956ef0fb662a2cbf6a86325a53dac8a": { + "balance": "28690160424000000000" + }, + "85caff4ec0e1719ad963e97c1c02828683070370": { + "balance": "2022427900763000000000" + }, + "8630cc2780fee566f172ed0437264c45421ce675": { + "balance": "669721278148000000000" + }, + "8633d245c5f1b63403e3d7828dc197ce1cfafc0f": { + "balance": "10000000000000000000000" + }, + "867ccceae3192a27751d870ae13b1d3d2c3584dc": { + "balance": "1491436265909000000000" + }, + "868bed241f77983ff4a7a8d0bf121299b6b2248b": { + "balance": "5600000000000000000" + }, + "868ddd283a76a26c8bbb9761df3ca647bea267e2": { + "balance": "9000000000000000000" + }, + "8696e546f96f6e51f405905e095902db8bb90118": { + "balance": "533558981421000000000" + }, + "86ac0eae4e4c20cb7019325f4dbebad053f92213": { + "balance": "697960117764000000000" + }, + "86bef47f9d2cd7526495454eb4d1737510696a5f": { + "balance": "2938307902381000000000" + }, + "86ddd4e3f444b395be8b2b2b75c35c78877fefb7": { + "balance": "15615434748526000000000" + }, + "86f115ed19a32aba4f98270b8ad45820abbc4653": { + "balance": "151868798605000000000" + }, + "870f19e7ee358de61ad0fd3c7710441156d68f66": { + "balance": "674715936435000000000" + }, + "87141a2d3857fb8a328ef8e7b503ed965294c85d": { + "balance": "1609607183158000000000" + }, + "87257783d866af25a7a71b46ea6c2bd1e9ab9596": { + "balance": "64000000000000000000" + }, + "87298979a9a0dbc272b0e15b7e5f2e42639c9912": { + "balance": "722087160930000000000" + }, + "8757b784015f88d072229176cabefa358a0e95a4": { + "balance": "204003337866000000000" + }, + "8760e60a56c5b8b61276634a571400023f08e3ac": { + "balance": "1000000000000000000" + }, + "877e54ea7e8ab03bb8e2b842dadab16bf4ae0a4c": { + "balance": "341020957932000000000" + }, + "87919285fc5d98169bbd073cebb1b3a564264dd8": { + "balance": "579080463078000000000" + }, + "87c39cfaa9c82d84119f306e6a233a3abfbb0ad1": { + "balance": "121753433796000000000" + }, + "87d479659a472af7c8ea78a3c658605f8c40bec6": { + "balance": "20000000000000000000000" + }, + "87d933ad6fba603950da9d245b9387880e6d9def": { + "balance": "1087642723520000000000" + }, + "87ec448309024bb1b756116b81652ea518cf353d": { + "balance": "344562808694000000000" + }, + "87fbbe010837f8907cc01a5bbd967f402a216488": { + "balance": "185411503628000000000" + }, + "8805a3c529bef4d19a6491f3b7d7b1b7232bb93d": { + "balance": "264150205918000000000" + }, + "880ec9548864fcd51f711ab731d847260ed0e3d5": { + "balance": "723225945994000000000" + }, + "8818d160b56b18e196871a6c7ccf02112dc13342": { + "balance": "2857439182291000000000" + }, + "8836e25baa08c19a9b0155c57072582b49f7dbef": { + "balance": "5468425690148000000000" + }, + "885b6303d06142accf2ddddbbdd4a9379d1cd124": { + "balance": "11853214736000000000" + }, + "88656958d9cd758d71546ba52c4ea646b658c84c": { + "balance": "10000000000000000000000" + }, + "88740acdf9ab5711d015391fe8cf4a7c70a0bc86": { + "balance": "510027156671000000000" + }, + "8874966976d776c3154261afa802692afedf3d3d": { + "balance": "305634301700000000000" + }, + "88aea53c727d7a5dd8a416e49faba1c4f741f01a": { + "balance": "15358334295959000000000" + }, + "88b67d05997ae3852259ca638a00ce9b9e7e4a61": { + "balance": "278125551806452000000000" + }, + "88d730e074a102048008de81d3adcba831335736": { + "balance": "5984576042159000000000" + }, + "88da27b1f0a604a87fdedd9ea51087a331179cb4": { + "balance": "10000000000000000000000" + }, + "88efaa91dab9671f5c903e69aa6ca4d9a04b5ddb": { + "balance": "1996126782729000000000" + }, + "89a9d702f64f14fae4d1a69717744dd700208d9a": { + "balance": "251686323241000000000" + }, + "89ac81571265bebbf9d3c09e9459fd1ba7fb1297": { + "balance": "162368080974000000000" + }, + "89c75c4f0ce41d283587beba1a3e3efab05ca6ad": { + "balance": "16000000000000000000" + }, + "89d44cb81cc5a1bdf4d573c4954ee641f3cb91d1": { + "balance": "97965629614355000000000" + }, + "89e2fef4f7b7c255b36afa81cf4033b22de3db25": { + "balance": "7278615226888000000000" + }, + "89fe5d3cb5283c7b87daf6103bb568f92a230631": { + "balance": "64000000000000000000" + }, + "8a07242231f4a654aeea65b857d1519385a18065": { + "balance": "20000000000000000000000" + }, + "8a5a415f0fe2a8329e14628493d11ca20d4e482a": { + "balance": "157274758238000000000" + }, + "8a6ce9f270fe3ec33a013be9e5b1ef823c0dab53": { + "balance": "20672772672000000000" + }, + "8a6fe4fa2f86f879ec9b2bf643beeb0876da46d4": { + "balance": "1041983771868000000000" + }, + "8a765ff2b429dcdf59b65a34c4bb41798dfb5886": { + "balance": "355487172996000000000" + }, + "8a9b9b65a3d443a6e4dcf696a64983f3b625774f": { + "balance": "3185351572575000000000" + }, + "8ab1f5443cf9149773b9ddb69de3e6ea047ae38f": { + "balance": "161619949415000000000" + }, + "8abeacee0078e07fb417277e8bf15dcc2cdb9fa7": { + "balance": "144000000000000000000" + }, + "8ac0d9e0e77aa4ada4080604f2118b3a5a0f8102": { + "balance": "100000000000000000" + }, + "8adae0dc99300f60d31bfa619ec83d45b48ea22b": { + "balance": "697262590215000000000" + }, + "8aef59e59a27a8662043f1a4abcaf945a5e3fafc": { + "balance": "26780431538000000000" + }, + "8b3386f32e2d77526c223ee8bb95b7dd111ced92": { + "balance": "2179932854210000000000" + }, + "8b34d5e457ef6451bb7f5ecc93c80678a30e3194": { + "balance": "31492358338840000000000" + }, + "8b47e07f192c33bd7d298bae717dfcd68a8097ae": { + "balance": "1000000000000000000000000" + }, + "8b55bff4b281f6a24ab428d66b91f9bab06f7b96": { + "balance": "1596248680941000000000" + }, + "8b576b1e2391f22193bb4f91bec5f2a8aec02af7": { + "balance": "29660301836269000000000" + }, + "8b9097b762c7bc38a487974f3551fea697087553": { + "balance": "260887123991000000000" + }, + "8b92c50e1c39466f900a578edb20a49356c4fe24": { + "balance": "35654824979000000000" + }, + "8ba3933337108841a997accf0b5735e005373f53": { + "balance": "574965182000000000" + }, + "8ba3eeb2d1b27e021ed6bf5827280807f32c7897": { + "balance": "64000000000000000000" + }, + "8bb23a5b8c48ec5bde84f39b463559b7c048c853": { + "balance": "16186405874000000000" + }, + "8be0b6ab14e15b46905335d07df03726fb1df0e8": { + "balance": "500000000000000000000000" + }, + "8bfc53af1ae6931f47ad7f7ed2f807f70fddb24e": { + "balance": "20000000000000000000" + }, + "8c0599df87df142d3aea37d50c975c1813ecb642": { + "balance": "871085782287000000000" + }, + "8c2deeeaf095be075a2646ed7b8764d3665acf14": { + "balance": "10000000000000000000000" + }, + "8c3e7381b0598356ff81e860faf25390ae7de9d9": { + "balance": "36000000000000000000" + }, + "8c5671a6f4610ddbd05a5139659c7190f54117b5": { + "balance": "50000000000000000000000" + }, + "8c60582c4e4e60da665b4a5a2d18f514ded6c49d": { + "balance": "16806447782991000000000" + }, + "8c8464ea6b17687eec36ef04966d59c7c91fa092": { + "balance": "1872124465602000000000" + }, + "8c85c5a318cc0227576adba3e91dce6adc73f6a2": { + "balance": "52479305517000000000" + }, + "8c8f3796a2942a2298d14ff1a9e3264e9f63f2bd": { + "balance": "10000000000000000000000" + }, + "8cec1886f2cc71b09ca32a1cf77a280ae3a6a9fe": { + "balance": "500000000000000000000000" + }, + "8d0b26d57eb52a62814d7876d64c8274f4371464": { + "balance": "20794037603000000000" + }, + "8d40b92e41f3cfec06e767d64b4dafc5612133b6": { + "balance": "25000000000000000000" + }, + "8d41ea1cfb70d0ef1f6572fd72a6b417739ac7dc": { + "balance": "738777348304000000000" + }, + "8d4eb54646f9d14882fc8ebb0ef15f6056d1afbb": { + "balance": "1003867239086000000000" + }, + "8d51ab29ccd190bfe12bcd94a651e9f49a003253": { + "balance": "442251355663000000000" + }, + "8d6c0c8e4ca47626115433b39feb939014b8738f": { + "balance": "119828137027000000000" + }, + "8d7acd92d664a485625bb9884e7cac9cc6077f41": { + "balance": "1381910232084000000000" + }, + "8d7ee7a9c1c263ba8061f54dcf62d9f8420e2008": { + "balance": "20000000000000000000000" + }, + "8d941c5d0c6e2b8e2934c9f80f8a63e2fb5868ef": { + "balance": "116443644149000000000" + }, + "8da0dc43ed3ccefb18f21aa13f3fa42c13e540a6": { + "balance": "516000000000000000000" + }, + "8dab4500316475e8fc3bb6494be09f549dedf026": { + "balance": "2736245677000000000" + }, + "8db39a95f4e63bde0bd8c02e386122ce2c57a30f": { + "balance": "12577347153000000000" + }, + "8dc718b49fb68584d9472490743f9be1b0ad683b": { + "balance": "50000000000000000000000" + }, + "8dd05e26224aa8a6deb0904b6d3bbb34d268e901": { + "balance": "613146658282863000000000" + }, + "8dda0e7ddde515480ef08cf90a1eb4e78f50a2c4": { + "balance": "19265526663314000000000" + }, + "8dedad1511c11798c338334dde7be967de96e9b2": { + "balance": "50000000000000000000000" + }, + "8df63c04f18a854d7bb397bca3e2ba19202e9da1": { + "balance": "1479940547081000000000" + }, + "8dfd7edb7d28e8b3df1faab70a8ef9e3b923d998": { + "balance": "10000000000000000000000" + }, + "8e2c3af057e931b5f82e83873b336a7f68e7eb03": { + "balance": "27138009123000000000" + }, + "8e2f4eaddd60468bdc09d47f65839b96f50596ef": { + "balance": "970529157231000000000" + }, + "8e750010c88ba99d75b0b5943c716d6fc0d01802": { + "balance": "42271114987000000000" + }, + "8e889d47f3307a18490e53f2108dc31b14d6300e": { + "balance": "115722965933000000000" + }, + "8e9e1953c82217ba56365e7a9c54b1ded73914bd": { + "balance": "6248835752208000000000" + }, + "8ec980d3066cb6afa793577cf88ccb46ce8d13f2": { + "balance": "100000000000000000000000" + }, + "8ef324c861de7e042c445776bcc8ac026533bc15": { + "balance": "1869634994148000000000" + }, + "8efd14464465e50af087a80a5fbe652445de373d": { + "balance": "1157403424927000000000" + }, + "8f1b57304406fd8b2eb5dabcbd322e326dd873f5": { + "balance": "194188733254000000000" + }, + "8f36ffd921e12083e374335d3cc43fcfeeadfa46": { + "balance": "100000000000000000000000" + }, + "8f813b88e6e125eab71a63455f326322ef505501": { + "balance": "19087691927734000000000" + }, + "8f83892d4d2892cd57828fde2318610a54b14498": { + "balance": "22833507983000000000" + }, + "8f89c1bcba85757cf1718d5b9eb007e27e5195ab": { + "balance": "2241600478705000000000" + }, + "8f927ab63df4c2ce46f1ea35bc875a0c006d2d4f": { + "balance": "327487123409000000000" + }, + "8fc3c231df0f93a84bbe348aff12ab576284d70f": { + "balance": "25000000000000000000" + }, + "8ffa089b07ed1388a5d1a428daf54d9591e734e6": { + "balance": "1347580402248000000000" + }, + "90040e00f585f8be44c82597037fde452472e741": { + "balance": "2746884591879000000000" + }, + "9034eb46aad2a76bdb812c981565d4701dc10718": { + "balance": "10000000000000000000" + }, + "904ca1ac2381702bd18472b175262a8928cde5f1": { + "balance": "304421909590000000000" + }, + "90502c1123692c3b86e99b328d07fae473d4a283": { + "balance": "227491252462000000000" + }, + "9052ca7e9623c1bbe3568668673d6d252b56a764": { + "balance": "35268091378000000000" + }, + "9093d12d8410193293e1fda0cca98a43b85b91a8": { + "balance": "6829489147119000000000" + }, + "909ba8cdc707c12ba577dcd8ed1df1c02a7ce2ef": { + "balance": "60524108169000000000" + }, + "90a2cc3aa73495531691e027a8c02783cea7941d": { + "balance": "65263780625000000000" + }, + "90d7c82615f151953a8d71a68096cee4d428619c": { + "balance": "298774379499000000000" + }, + "90e02deb31d98b9c85fcaa7876eb5ec51d721dd4": { + "balance": "2000000000000000000" + }, + "90e538746bbfc6181514338a608181a3c4286d1d": { + "balance": "6069511690189000000000" + }, + "9106dddc1b693e7dcb85f1dc13563d6c7c9d8a6e": { + "balance": "1977000091291000000000" + }, + "910d1e0d3f71054835ee0d4cd87054dd7add3e38": { + "balance": "40104690362000000000" + }, + "912e2349b791fe702692a6c1ccbf6f0f06b826db": { + "balance": "6305336897000000000" + }, + "9144cc61c01eb819e654b46730620c230da9e936": { + "balance": "144000000000000000000" + }, + "91478d4c15d9ba02816456030915be08fa3aa208": { + "balance": "200078339107000000000" + }, + "9160c466b5f9020b0ab1c0ff497bf0345598ec90": { + "balance": "17705350930000000000" + }, + "919025625787101c572d8340ead1444a96593424": { + "balance": "2418027749789000000000" + }, + "91926323868c65f91b6d74c85c07279610651ede": { + "balance": "538073886450000000000" + }, + "91950cd6e2dd99e024854b65c09c5a7476777a21": { + "balance": "11629505934425000000000" + }, + "91ae8d74c26d3dcc291db208fc0783347fcc197f": { + "balance": "7604593786920000000000" + }, + "91b9ac26869abc9eb3090f1d8140eabe97f41001": { + "balance": "25000000000000000000" + }, + "91c349651afb604f9b00a08e097e02c0964e148a": { + "balance": "117290771022000000000" + }, + "91ddc95cadeb6dcf6ebbdb3300a29699ac8ded39": { + "balance": "20000000000000000000000" + }, + "91ebbd36714cc069f8ce46f3e0eda5504fdd3aa2": { + "balance": "203944728497000000000" + }, + "91f2765125b84923bd506a719d72b0c1de030e32": { + "balance": "452269960816000000000" + }, + "91f2e54a9d61ef52a33d150da50d5a8f2ebcd6bf": { + "balance": "242321058694000000000" + }, + "920dc90d11e087a0d8912c1d43db102e9ba4f43e": { + "balance": "20000000000000000000000" + }, + "922ff522cf7f3ce0bab9312132df51704caa755b": { + "balance": "1414824682473000000000" + }, + "9251449b0f757ef62f63c2774eb63ba15bf3712b": { + "balance": "102688517037000000000" + }, + "926255c17386720fdc1701747a2f024475063d4a": { + "balance": "25000000000000000000" + }, + "92808a38ffc5a339b1ab6b0b472f9975718d4a07": { + "balance": "500000000000000000000000" + }, + "9286c4497e820845341e3b9127813c1b7c884830": { + "balance": "101241387488275000000000" + }, + "9298e1df6730e91e9892d19f7ce18a3db9b5d2a1": { + "balance": "169000000000000000000" + }, + "92d98aed335c29402a43ba96c610251bed97308b": { + "balance": "3032350763000000000" + }, + "9319153f24814a81d920c60cbee9b5f2f275fac0": { + "balance": "56619610984000000000" + }, + "9347532d6396bc0b86bcd34eb80facd4c3690684": { + "balance": "258912194626000000000" + }, + "93487691d71e6248d88f06b1fbaee58b6fe34615": { + "balance": "1593901704394000000000" + }, + "9375154a7f19783b26ae1c9e48f114e1cfd1307a": { + "balance": "9000000000000000000" + }, + "9377947e0db688bb09c9ca3838ca2197fb262a1e": { + "balance": "323993393587000000000" + }, + "939ca9030b28d356dc1b071169f98b0728a9aef3": { + "balance": "218900305967000000000" + }, + "93b71636b8332515c2af031aac7a8805de716a62": { + "balance": "1640174743698000000000" + }, + "93bca153afd427b0c3c1de4a5584610e4a6595b7": { + "balance": "654782426410000000000" + }, + "93cb3b73fee80cedacf5197f8b4ac8f18f0d0184": { + "balance": "100000000000000000000" + }, + "940fcd215bab373d1b736e354f2def501244885a": { + "balance": "13133641534585000000000" + }, + "943f4bc76f20580b6546b6aff2800448f82cfdc0": { + "balance": "1927982550280000000000" + }, + "946ddb5c46fb13010b9c7ec56e4055b4f3e24b4a": { + "balance": "1410000000000000000" + }, + "947961dc367226f78d722361d5821cced52db01b": { + "balance": "115598797369000000000" + }, + "948eab3ffe44d5f1f381de2c8cadcb311c25df2a": { + "balance": "870664355820000000000" + }, + "94bf674593378243fb6b811f331f77561efb4106": { + "balance": "226539311455000000000" + }, + "94ce082887dd6324d7dcfa6cae17b653be021b25": { + "balance": "420000000000000000000" + }, + "94e2aaa4b5e2b36a12f866c96e3382a1150a97b4": { + "balance": "7344059136611000000000" + }, + "94ea5b1cdceb3f1a9d5ecacb6ac8dd2db9a461d7": { + "balance": "1951787237292000000000" + }, + "95218633176c0fe2f32fb55ad3df9f387e63aed1": { + "balance": "99999999580000000000000" + }, + "9543cb22853a46cce3aadc60e46cbddbd3fcf593": { + "balance": "2806074281914000000000" + }, + "958842c5389656d156aab05ac1731a20656716ff": { + "balance": "391064461038000000000" + }, + "958fd9bbc96531a00adc5c484d06dc61ccd717b6": { + "balance": "8021794447667000000000" + }, + "9593ce72919cb0648ddacc58af233d942963e2e6": { + "balance": "32322940730755000000000" + }, + "95a8e371af9128c97c9d4d7c4d58f5f75f2d07d4": { + "balance": "49000000000000000000" + }, + "95b9a9ad563a4c1ff7b6ebcf5fabcf5dbdb4a6a3": { + "balance": "10000000000000000000000" + }, + "95ef5fac6aa3ab1b4a87246fa800cfceff43dec7": { + "balance": "119666779022000000000" + }, + "961a3aa8015cd520de43bd47d81f5194ee4dfdc2": { + "balance": "248589901007000000000" + }, + "962bad39df25d64ee1c6b4ae9c14a18d316bfc06": { + "balance": "2404608291000000000" + }, + "96392119198c4b644c64284c9a75f61210a6292d": { + "balance": "1000000000000000000" + }, + "963c82319380587eeba0bd7b07eb63ea7042984b": { + "balance": "1480123630618000000000" + }, + "963e05fb6245ec11d67ed80e9feba6e2c0a8b4ae": { + "balance": "276053287417000000000" + }, + "964452b86b0d1d4b34aa881509a99e7b631d4a85": { + "balance": "64000000000000000000" + }, + "9644a2af2ff70eb43584a4351bfbe027c42ba3f9": { + "balance": "500000000000000000000000" + }, + "96572a017489450f2dfc0e31928576acd3bc6808": { + "balance": "1140183097730000000000" + }, + "9686bfcc0dc3de20604eb77787d0dba818cc5016": { + "balance": "10593448987804000000000" + }, + "96879780764b4433589d26573fc221f5218f1877": { + "balance": "154136576560000000000" + }, + "96ac1e62c95e33dbbd4f6ed389007e16c00b205e": { + "balance": "4130528000000000" + }, + "96ba703df3a8a6dc3c5d6be02cbf6a4afa2d1650": { + "balance": "2885298549532000000000" + }, + "96d516ded110f1d7e0290716689fd1b7964d9d42": { + "balance": "40665675241000000000" + }, + "96d75950c9354cec6084ba11058dd52d00fdb1f2": { + "balance": "903158106646000000000" + }, + "96f362c59c72fa1d39ae3ec37a7b715d2dd23679": { + "balance": "110000000000000000" + }, + "97115f7544cb05009b3fad2f0c2817f3ee77dd4d": { + "balance": "10000000000000000000000" + }, + "971cbaeafd4b0fdbad24fab946051b8949efaebf": { + "balance": "8462381628000000000" + }, + "971e195e980b4fd4db8d279c80968ca1bd390edd": { + "balance": "10000000000000000000" + }, + "9722648970c455929d621546fddbff27c49acd3c": { + "balance": "70337427969000000000" + }, + "9772027a4ea991eb9eb5ae6b8f34d750a917538b": { + "balance": "148918416138000000000" + }, + "97797e3919aa35567b9eb1224be87f96c6c2e1b4": { + "balance": "973342196399000000000" + }, + "9780a9c86160e27f627179535c3d3f23b6b29917": { + "balance": "10000000000000000000000" + }, + "97a85f4e3f53aa066825de15f1d0e25d4189b037": { + "balance": "2435764858719000000000" + }, + "97f46465e99910539bd3593c16a572e159bac87d": { + "balance": "25000000000000000000" + }, + "9882505fcb54ca2d2f4f79b03f0a5ead61936979": { + "balance": "249999580000000000000" + }, + "9898e969629502a891b758efecc9fdc5ada7d32c": { + "balance": "20000000000000000000000" + }, + "98a52d325e28ca9b4474846c7e4c07a223440fab": { + "balance": "418260286015000000000" + }, + "98a9b2f7d1ba7838e3242b5e4cbf1f2897aa4bc5": { + "balance": "500000000000000000000000" + }, + "98b8308c37a2f6cc1bb382dba2ba95a3c5ca2834": { + "balance": "10000000000000000000000" + }, + "98bf0170a61f98ab0710a68810bf152b7f6c56fd": { + "balance": "2279761566089000000000" + }, + "98c8a323a0022bd147a466fa1ac867977e12eb92": { + "balance": "10000000000000000000000" + }, + "98cd102caf0866ba0a74604b01f54049503905d6": { + "balance": "34739921273310000000000" + }, + "98d7e89c2765aaac224d4015aa277fef208953c3": { + "balance": "1291811952000000000" + }, + "98fe96bfd1e10fb60b343e512b15e955aefc0778": { + "balance": "464922897623000000000" + }, + "99064a57d693e45559a1a910c9ef7d46cce0e703": { + "balance": "8969733492948000000000" + }, + "991ea5429b91a8bfc4352a1d93304dc463be5b90": { + "balance": "149367286734000000000" + }, + "9921d405fada890fee6bf76acc39141fd34e5d2b": { + "balance": "5021308706457000000000" + }, + "9938d357d3d5dcc6f6fc7fb47a98405c0ab6830e": { + "balance": "516293591974000000000" + }, + "994f4e6521a3a5752359308b9f6b2722922c60b1": { + "balance": "23993133615000000000" + }, + "995a6a1c38f037b3a9f0a2e915b8fc0efdea082a": { + "balance": "1403498530728000000000" + }, + "99709e57748a7da6556b1670ba4f15c45aef4689": { + "balance": "36000000000000000000" + }, + "99789f65655c6f917d575169f4ba8192440e659a": { + "balance": "393071814319000000000" + }, + "998f66cbde2693603fa109ad7aaa8bc42a8765a9": { + "balance": "49000000000000000000" + }, + "99afd42a58af31daa54ad9ba35b06954330107ba": { + "balance": "25000000000000000000" + }, + "99b6a9ff2b2ac9ac0361af007aba107695ff5fad": { + "balance": "12860157225353000000000" + }, + "99d16a5955d43723ed8e2b1a642f8f1195f38b64": { + "balance": "62907829047000000000" + }, + "99df609926ca536ed3be80e35dbaecc42ae67f2f": { + "balance": "316809833612000000000" + }, + "99f3faf97a36fabea7306979b30b08fa70110e29": { + "balance": "173292373556000000000" + }, + "9a26110067b473e3bdc0fc32951b39596c967a56": { + "balance": "78192198764000000000" + }, + "9a3a8eff6fb82377da6c17ba658dca87ca0dfe26": { + "balance": "50000000000000000000000" + }, + "9a3b06257088ef8c17410a8f2d63392edb9b55ce": { + "balance": "239567000000000" + }, + "9a426842301802866cca0ef89794d928d3e8f843": { + "balance": "776173297821000000000" + }, + "9a5f2c0a6d41131d9aacdb4f8c274958cbdd377e": { + "balance": "441954000000000000000" + }, + "9a6893023ac6f34b493d33e4dc63ef697169a58d": { + "balance": "439689418527000000000" + }, + "9a86eefd848acafcbd9960003e90b22162b15ef9": { + "balance": "294190908093575000000000" + }, + "9aa711f3e4eb67d2f6405b5ee6290a014d203a72": { + "balance": "9101556549634000000000" + }, + "9abf9ccf6abb8d55ede458d2d12a279d0a823944": { + "balance": "17609693072000000000" + }, + "9ac1909b983c754f0800559174025c0f0baa9d31": { + "balance": "80921948093000000000" + }, + "9ad62cd855d629e1ddab632874a6dc2b812f2348": { + "balance": "2068118534000000000" + }, + "9afc2c33aa2c9a42600abb18aedaefa433326122": { + "balance": "2485353229354000000000" + }, + "9b18d230b221a99c74877d4a1dbdee2214c7d60c": { + "balance": "4024172228743000000000" + }, + "9b18e27788c9d59053072032a480569e142595a0": { + "balance": "110789164888000000000" + }, + "9b4535b23af0b8e5f488a6f318ff6badf71d16c1": { + "balance": "84756740661000000000" + }, + "9b5e7cf43aece7b38ea2af6d08bebe2d3b926840": { + "balance": "262771268227000000000" + }, + "9b77dac92fedd0ad3eb4326d4fafe0f4315a8844": { + "balance": "3616321626000000000" + }, + "9b8f6f223641f9b1bab319dd1e88c49fd411a765": { + "balance": "2054417462086000000000" + }, + "9b9f94861d80365464912e5c7213403405a6cd8d": { + "balance": "2367093088000000000" + }, + "9ba24397002929e6239848596b67b18a8dea1eef": { + "balance": "5000000000000000000" + }, + "9ba99736c5ac468d6b644e39b8d515c39151f51d": { + "balance": "311900650761999999999" + }, + "9bf2d4ff366e1bb2313ae9a93ccca75d6bc0d232": { + "balance": "764870206925000000000" + }, + "9bfce7dbfc9ae62d450e862261d1e21e68bac92c": { + "balance": "1000000000000000000000" + }, + "9c003e74b62f192a864045d678639580c672fc22": { + "balance": "50000000000000000000000" + }, + "9c128bd2c0c96b896db6c0f398e908c98302809e": { + "balance": "3251059363800000000000" + }, + "9c255daa89ee16f32fc0ab1ed8e22db39342e6ca": { + "balance": "37695843594589000000000" + }, + "9c32e714bcb601a56a8a4e6b3f7bcd9e1c7a1b54": { + "balance": "50000000000000000000" + }, + "9c503e087b04a540ed87056c9371d591afa72df2": { + "balance": "64229084991000000000" + }, + "9c54297dd3527cbbb8ca8c305291b89bfb7ab39d": { + "balance": "61682466962052000000000" + }, + "9c55bb1db3b2bb06e605a66ced9ea2ac95718205": { + "balance": "16512365324000000000" + }, + "9c59dbc48b9cf53fe668784e89d30493da9995b3": { + "balance": "50000000000000000000000" + }, + "9c61b58aa760265f7fd1b9e749df70122ea81175": { + "balance": "50590272373000000000" + }, + "9c6c7eaf4bec0566a7bf8acd30e10311a963267c": { + "balance": "999999999580000000000000" + }, + "9c91dd4006f9d01d8caf5f5fb4f2c4f35ee63ffc": { + "balance": "175730980227000000000" + }, + "9c99275f5dee14b426302b1a47a8488c16432f2b": { + "balance": "2000000000000000000" + }, + "9ccf7b23528d062da63f6af3e26531b775c83c52": { + "balance": "928373869120000000000" + }, + "9cd21c30ccbc1087c9b351395fdea17ad669cc2e": { + "balance": "529762292313000000000" + }, + "9cfee47d6f24880af7b281cc00e1fc58e0a4a718": { + "balance": "198888257958000000000" + }, + "9d08251f7d4cfd66d15c17e1ea6bae5c795e290b": { + "balance": "813841349140000000000" + }, + "9d5411490ce89359bfbacf9f9957ebfbbc18debb": { + "balance": "22263187467000000000" + }, + "9d61e1dfaa7d0e0c5f5d733a24a1883c4e201f3d": { + "balance": "144000000000000000000" + }, + "9d754d94a15ab6d738e511fe4c775ee6d20a53ee": { + "balance": "20000000000000000000000" + }, + "9daccedf104fdcc3c39f2961ddfa1c64eb632476": { + "balance": "1237093270947000000000" + }, + "9dad4968c0e44aa729fc5732f3ee903c6799637b": { + "balance": "838788687517000000000" + }, + "9db73ca677bacbb622f44fe90b53ee1d9f0c2009": { + "balance": "472858335000000000" + }, + "9dbcb5026e0f444a33197da240856f108db14ff0": { + "balance": "10000000000000000000000" + }, + "9dc46cf729187ceed8001c4ab14fa4fc21c35f32": { + "balance": "3320792646995000000000" + }, + "9dd895c1bdac2ed9864134aaa8c543473ee5f19b": { + "balance": "1430620966869000000000" + }, + "9de2687242cbf9fb94fee0ad873acc7494ebd2bf": { + "balance": "20000000000000000000000" + }, + "9deec036282717aac93ad5cc1b6d4a5354e85c2e": { + "balance": "2048627955362000000000" + }, + "9df8dc66395aeae9b4c831b4d63bdf48db08811a": { + "balance": "215874670561486000000000" + }, + "9e1fe68a70abd8ab517878b03961da8564b43eb5": { + "balance": "67908329894526000000000" + }, + "9e33293006982abc668e199aab20260b9b754463": { + "balance": "49000000000000000000" + }, + "9e65616282a0baf89469a58915fd8fdbed210e3f": { + "balance": "829209872657000000000" + }, + "9e7b7b522834dd7e83ff2bb6b6e4cd2972330899": { + "balance": "500000000000000000000000" + }, + "9ed134b3a8feccb4056b2e511cea9a8ec58a3e77": { + "balance": "18787546978390000000000" + }, + "9edcf477687a9dee79341ed5d89d576c9a854c2d": { + "balance": "500449025554000000000" + }, + "9eeb06d4b532118afa013a01c9e89216fe0475ae": { + "balance": "1823939486758000000000" + }, + "9ef20a338e85044922f08f3648355e834874d551": { + "balance": "50000000000000000000000" + }, + "9f0855f9cc429fd3590c6ad05bb66a9e038efdca": { + "balance": "8017999878252000000000" + }, + "9f3befcc1884d16b65ae429228d26fffc146c8dc": { + "balance": "1016482445089000000000" + }, + "9f4571748463eee19e59ff9bd734a62a66613850": { + "balance": "20000000000000000000000" + }, + "9f51de282745f77b8e531e1de0b7c14e3369ba54": { + "balance": "1010657089383000000000" + }, + "9f6527175a2b581cc79f2a68c35202e0a7f2af20": { + "balance": "216495522463000000000" + }, + "9f70204d1194f539c042a8b0f9a88b0a03bbcd8b": { + "balance": "10000000000000000000000" + }, + "9f70e44704049633110ecd444f9540e241b50783": { + "balance": "9139000000000000" + }, + "9f73fea741e8506ba7acb477745dab1cfab8366e": { + "balance": "4461472359634000000000" + }, + "9f88d33d26c90e74c39c9676b8b580d21bbad124": { + "balance": "54437240781000000000" + }, + "9fa47455be14ad2eecce495281ed0eea926ec6a6": { + "balance": "10000000000000000000000" + }, + "9fbb15b595d154754a2ae77c77283db9d4e9f27b": { + "balance": "6195722646556000000000" + }, + "9fc480ab1823a59fd6130c3948980f95ac99f1d2": { + "balance": "24101151540000000000" + }, + "9fe5f054165fbf1943b1b02c01063f04e0c3890b": { + "balance": "1000000000000000000" + }, + "9fe7d3d5976e7b8b5ad6baa15ceae96c43c60fea": { + "balance": "55000000000000000000000000" + }, + "9ff116ea0e219814970cf0030932f5ce2cd9a56f": { + "balance": "36000000000000000000" + }, + "a01f6c36193839bc3a31e6d0792324771040fc05": { + "balance": "48298750000000000000" + }, + "a0264706d668522b737bbdbe949ce3e5a60fe314": { + "balance": "1423066922869000000000" + }, + "a02b13bb3b13027045ffb9b44bc7380a942e8ebb": { + "balance": "86845430807181000000000" + }, + "a03d246a931c3d422e5d2bf90f64975923a93643": { + "balance": "5834171660287000000000" + }, + "a046caaee59425ea1040867c62a6fcda11652a23": { + "balance": "83087966538000000000" + }, + "a04b57b2dd8b2082c53517d956f5909d25e14b69": { + "balance": "4518538234851000000000" + }, + "a074ef9e0ffe15619103e3de490f5813be53dcbb": { + "balance": "4568113810000000000" + }, + "a07bae42b44c085067de16e7d9846db529059acf": { + "balance": "4000000000000000000" + }, + "a08530e5fb7e569102b2c226aa5e53dc74483e4e": { + "balance": "2325665286793000000000" + }, + "a095a2c666f4f3203a2714fb04867c13c2add4be": { + "balance": "14768043990000000000" + }, + "a0a967418a3fcb3ee3827a08efa851347c528a60": { + "balance": "20000000000000000000000" + }, + "a0bb293071e07418ecb2fefc073136569ebd1736": { + "balance": "25871128320000000000" + }, + "a0c6c220a53b7dc790f7a5b462a106245c761f70": { + "balance": "1000000000000000000000000" + }, + "a0f06c86c49b248f4835bff405b620d12ec80d07": { + "balance": "484572382390000000000" + }, + "a10bc9f4d05678b26c4ffd2d92ab358163020b61": { + "balance": "10000000000000000000000" + }, + "a10c1197f7bc96639d01a652df73e49c669165dc": { + "balance": "1205859101575000000000" + }, + "a1221b2001f85f71e0655551e300ce115284b8dd": { + "balance": "1376698025177000000000" + }, + "a13fce836d65124fe5bcfa2d817ab2a043acbcf8": { + "balance": "55000000000000000000000000" + }, + "a15f1f609f7464906e0eb9d5e1d26468b90d9198": { + "balance": "16000000000000000000" + }, + "a1617dcf3acda60737e5ca9e4d0ecd82a98ef667": { + "balance": "500000000000000000000000" + }, + "a165c5f151d0daab905ba4a6d1fe5d5114fd7686": { + "balance": "41039049526000000000" + }, + "a17d5bed36c1059561e184a8a90a38ce955b92e4": { + "balance": "10000000000000000000000" + }, + "a18efb4e0950e7ac95970cd4591dacc286241246": { + "balance": "12403188476000000000" + }, + "a191fa6be64f2f6d2b4a7fb5a586416a605552c6": { + "balance": "60340281461000000000" + }, + "a194c15518cefbe94edbef3a2421586b51f7e1f6": { + "balance": "4153525550636000000000" + }, + "a1d0e41aacf83fc62fbecf35f8e873f8d734ecaf": { + "balance": "9000000000000000000" + }, + "a1ddd1f615ed483ef895e341f3266b6891f9b59c": { + "balance": "180411786335000000000" + }, + "a1f4d1e03114707a56ef9069bc20c6094e810d34": { + "balance": "51949145435222000000000" + }, + "a1fe101a65616cd03e3af03092be63434b7bf203": { + "balance": "1005401878265000000000" + }, + "a25a8225ce67c54048737601eac5e0d063c2fa17": { + "balance": "272038848571000000000" + }, + "a2714999233bcaff7294fa3e3b64c63ad45a928b": { + "balance": "14560781294000000000" + }, + "a28db3f7fb1771a3d77dfb19b54f88fd55b15c8c": { + "balance": "8000940572576000000000" + }, + "a290101bfe5fbc73146c4ec3ab5266c043eb701c": { + "balance": "1397563244603000000000" + }, + "a2924cfbcd37d0b321d6abbe57c645f9ce32340e": { + "balance": "200000000000000000" + }, + "a2a26c34f3d950c795fc965f6b1df3990e111403": { + "balance": "34525064429023000000000" + }, + "a2a2855851711bfc051c1f298821ae89e4c872c5": { + "balance": "491025000000000" + }, + "a2b956dd6f1934a4a44a026a18ac345ddabe42d5": { + "balance": "20096625821563000000000" + }, + "a2b9a118a79be81711d95485aa12e3efe78ca256": { + "balance": "10451051632647000000000" + }, + "a2bcf08ddd1778b30ea7882518148edfba2d9b20": { + "balance": "347033754668000000000" + }, + "a2bd489ec4790f4145f8a9a95c9c829c5c020146": { + "balance": "100311110878000000000" + }, + "a2ee35300ddf6a2491ec0e1848f8b56defafd7fe": { + "balance": "500000000000000000000000" + }, + "a31adf082ffd212df18d5a84b105a937e83b1b1a": { + "balance": "7124891785000000000" + }, + "a32c944e6c5fe186794b88d6bcbf51c47bea55ab": { + "balance": "732129357042000000000" + }, + "a33105d543f5d2b1220d4e1ecfdcf85699324dad": { + "balance": "74798779358000000000" + }, + "a3330c73e2d79355a14e570da1ec2e80f8048c69": { + "balance": "10000000000000000000000" + }, + "a3580034590e3052b9de5abd635e514ec5ba8694": { + "balance": "10000000000000000000000" + }, + "a360d8e2519dc6d7793cc371d91ad6add75e3314": { + "balance": "192622260840000000000" + }, + "a36b9b8b2adb20fb4a84d3025bf2e35baa8b7fef": { + "balance": "20000000000000000000000" + }, + "a3771b191237bef48339aa77ad5357f6227b358c": { + "balance": "512633055119000000000" + }, + "a3892bfd25705387cfb4eeb6d21089753c22e3e2": { + "balance": "258136912825000000000" + }, + "a38c793775ebfc7330b4331fe2dc848abb862b73": { + "balance": "1193250172232000000000" + }, + "a39417002ab94845541aded4a614a5a04af8187b": { + "balance": "1185722898000000000" + }, + "a3a79a9f929b54075de43689adb665ef914812ca": { + "balance": "100000000000000000000" + }, + "a3b59ea3d366f818ca09980846ac533d4685c121": { + "balance": "59734700360000000000" + }, + "a3c7b7c594a64225922e02039669e4d0b43fc458": { + "balance": "11779233750000000000" + }, + "a3cc39a68184e51f6445d3ba681a55f4157d4383": { + "balance": "10000000000000000000" + }, + "a3d414d9f210f7b77f90790ce09f6128abe50adc": { + "balance": "10000000000000000000000" + }, + "a3dfda16e5ae534ac100f56741b77b6f86786615": { + "balance": "9000000000000000000" + }, + "a3f79b9d1fc9d6dbaaef49d48fa9c9fb5a822536": { + "balance": "108910000000000000000" + }, + "a3f87414bc9e6f01c2fbde966fc8fb6edbf58c29": { + "balance": "441000000000000000000" + }, + "a3fa3f58c802d9a9690de760716275f14449045a": { + "balance": "437227558095000000000" + }, + "a417ec5a9749064a6521ca2bf9d05f208eeaed54": { + "balance": "959205202638000000000" + }, + "a484d5b883d2b99b81b7bef27e307957ecb64b15": { + "balance": "126491152120000000000" + }, + "a488cd48258e57d66f44e73a60c121f963cb29f5": { + "balance": "20000000000000000000000" + }, + "a488e3b5096e964b21cdeba12ab423f391765b6d": { + "balance": "1712050478592000000000" + }, + "a49dba65f28909e9bd2ce5675bd091f498c6c5db": { + "balance": "216802821062000000000" + }, + "a49eb6a791022c1324facc23d8813f9954d1c639": { + "balance": "287438914902000000000" + }, + "a4cc080a5c4649f511b5844a8e0b031927e13a87": { + "balance": "20333578449000000000" + }, + "a4d2624ac5e027f72edaa625ef22134217203b5d": { + "balance": "1000000000000000000" + }, + "a4d30e35c9617eafeda82866c96c3ce6bf14400e": { + "balance": "1223254927978000000000" + }, + "a4deae7355bd2e1d57eefa56600601b8b475a501": { + "balance": "36000000000000000000" + }, + "a4ff3b5abfe4e50adad16d01aaf62c3d4cdb5260": { + "balance": "20000000000000000000000" + }, + "a502b109869ef07451576bf0e13ab294e1f236b9": { + "balance": "94843398055000000000" + }, + "a517a3b5e4324197902e16f8a29e47335cf39c11": { + "balance": "100000000000000000000" + }, + "a51e101088da23c82907e3e2c65a058f0454b131": { + "balance": "196000000000000000000" + }, + "a52bcff6a7e2e70cd714058bc30a16138fe39899": { + "balance": "30429750204000000000" + }, + "a544e84c2bc4b17859d06f136b6e377e4e398b22": { + "balance": "143977568178000000000" + }, + "a54ddacbc17a98b9fb6292aab3d92f4c5753fd0a": { + "balance": "100583192014000000000" + }, + "a557754f6637a19c1a48cb9bf58c1fe897acf434": { + "balance": "2087038692036000000000" + }, + "a56649205d9ea247b49e03dacbed6c78c21beb4a": { + "balance": "5046177099585000000000" + }, + "a568136446ee6b3bf62a20238db3b11397a065f2": { + "balance": "11652249158033000000000" + }, + "a56a7865b526e315a9eb41f4847485c7e0c952fd": { + "balance": "50000000000000000000000" + }, + "a56bab2a9aac9d08a7bc9265864a80089b68570d": { + "balance": "37138466291329000000000" + }, + "a5965a601c5df7765cd70e5dad27dd23da67ac99": { + "balance": "10000000000000000" + }, + "a5a3161c44c34c441784b7df795067760b0ee569": { + "balance": "35053289069000000000" + }, + "a5c245cf843e691956007b94e259b437a4e6b7e3": { + "balance": "18749166170000000000" + }, + "a5d7de961c3b991dc78f2d6c0448fa6225116d3f": { + "balance": "1574510758868000000000" + }, + "a5f47d2081ef728808786128549a28a5662e92a8": { + "balance": "1750000000000000000" + }, + "a610c90f5b7e5f33044956ba431a3887de1c969f": { + "balance": "25000000000000000000" + }, + "a61c1919bc3f3181dc94e2230d35574cfc972d78": { + "balance": "8990565120000000000" + }, + "a62f1aabd91cbc0112e796d1ec3727fcd26fa293": { + "balance": "1311277302001000000000" + }, + "a64fff0bb32e32f81a541c393982bc59fa183b1e": { + "balance": "8291357610655000000000" + }, + "a673dae555d367b8d4a784274577a1884615b9d9": { + "balance": "27416452091330000000000" + }, + "a6c780b585355d84d9d3c13be5bd05374588e240": { + "balance": "913657165911000000000" + }, + "a6cc1f6f51862c2798adaa1d266988022005a71a": { + "balance": "284500645805000000000" + }, + "a6d9c82784fa20dcf28266d047db441cfeb8855b": { + "balance": "10000000000000000000000" + }, + "a6dae08f99e4fb57b066a645a259d8e4f7ac2bc8": { + "balance": "9044922773690000000000" + }, + "a6f49f36f8d10a796bc2afc9e069cb0c76004ddd": { + "balance": "128555691078000000000" + }, + "a721ce1c294a0f1957ebf9be20b0fffcf90111ad": { + "balance": "3392103457630000000000" + }, + "a72b82c33bd3d6060e8a04392d236775d48ec3ae": { + "balance": "1434465940701000000000" + }, + "a7344654f2a1a44b3774e236f130dff8a4721e82": { + "balance": "100000000000000000000000" + }, + "a748cced92a87066db8b29f931fb92e827488a9e": { + "balance": "5487679824758000000000" + }, + "a78dcb2bcbec2d0a60661e1715c9a95c9d573a68": { + "balance": "346798292989000000000" + }, + "a7a6c0505e7090e0b2c21394877f91c50be6b45f": { + "balance": "4125233658872000000000" + }, + "a7dcdd9b9785a44a2dd4c5eeeb863ac1feae0f66": { + "balance": "10000000000000000000000" + }, + "a8013e9dca1bd38975748de2fb6cb3af5cae74d9": { + "balance": "10000000000000000000000" + }, + "a807bf78b15c15cd9e8edcf586849db716fedbb1": { + "balance": "1458293606310000000000" + }, + "a83410ff00fb4b913dd0ea2003b38c5c3247350a": { + "balance": "2876442029807000000000" + }, + "a848f61298a409e77a03900712017572f35a3319": { + "balance": "2783106133600000000000" + }, + "a85bb81d0dc57f824a763814759fd93fe3020569": { + "balance": "4558027813744000000000" + }, + "a860611cd098ce98974313030d9f6f462bb274d4": { + "balance": "961594154368000000000" + }, + "a8799eeff72929ee6cbfb5b0c02985cd4841be3c": { + "balance": "500000000000000000000000" + }, + "a8c29b9b1349fac0be9a65873e1911b7439c9a63": { + "balance": "1264035560749000000000" + }, + "a8c321024a3c015d881efca33bd1b2c1788b379e": { + "balance": "528752788000000000" + }, + "a8d02e8925ed48f4274d8bee62253dc0d4f2989c": { + "balance": "209083880937000000000" + }, + "a8d2bde2ccd6bad67ee1b9550c9310accb37cd79": { + "balance": "49000000000000000000" + }, + "a8d61abc6a403adc183aeb74c83e4221fd28ee1e": { + "balance": "50000000000000000000" + }, + "a8eb6aa5a0c5b6d9260a202dc76ab674d9a5f3b9": { + "balance": "1041257515142000000000" + }, + "a8efc57efc776dcaaf4003a8cfa63f215ab0284d": { + "balance": "166144142685000000000" + }, + "a8faba86d87678294e311cfa7f8cbeb6f9d8a499": { + "balance": "124541781000000000" + }, + "a912e02f8eab0cb620316129875f919455201117": { + "balance": "6454482105955000000000" + }, + "a929ac95281d1a77a3eda3b5ac90a761ef03ff16": { + "balance": "1074305309650000000000" + }, + "a92a4e40519003813f5574397ce328d046f75802": { + "balance": "9188437500000000000" + }, + "a93850ba8fff3bd18ab259f87c58bbce84165fff": { + "balance": "39018890852058000000000" + }, + "a9843660a17c2d972246028cb8045472abdd346d": { + "balance": "1052681604185000000000" + }, + "a9866c6271733971e46df3c9bb27b3d3c513c166": { + "balance": "200000000000000000000" + }, + "a9b1299c0c064e766f9f29f4301a78c6e4931fcd": { + "balance": "267785134400000000000" + }, + "a9bc33b9c99dd5a3967387c1e99766f9bc74d591": { + "balance": "65356157048000000000" + }, + "a9ccf1cd2f816b15182997e3207d9a681bf21b06": { + "balance": "17521053440000000000" + }, + "a9e54bd9826f853f65e0be1ec0bb9c28f95e0eea": { + "balance": "6260000000000000000000" + }, + "a9ef563c872342f49817a903a5725b504d455ea9": { + "balance": "50134015139000000000000" + }, + "aa0d69c7e1382cd16c527a3fee48db19c38e1398": { + "balance": "142562301500000000000" + }, + "aa12abcc3ab373d07bf560fd200652c8580fd967": { + "balance": "5509242259903000000000" + }, + "aa1d6b968b3f8046a94f128864bfc612fc2e2700": { + "balance": "489179780895000000000" + }, + "aa20b8559d6dd1543e8c528775ae4b04c6242471": { + "balance": "169000000000000000000" + }, + "aa227e9d6074a60ecd43e1cc24092ee58560374c": { + "balance": "596190898010000000000" + }, + "aa7b660fec7b05968ba656eae9a8aaef4481720e": { + "balance": "674642002744000000000" + }, + "aa9e04077d44d288978a3a3ab0d7c251c0447a4c": { + "balance": "10000000000000000000000" + }, + "aaaac1e72955e9d67625cf8bed73fa643fb1cc1a": { + "balance": "9781187987000000000" + }, + "aab46c0c2db4e330834081f97678906252746f97": { + "balance": "16440184245000000000" + }, + "aade5358c52b8aa5ad8ff285c6b297e86f49fa0f": { + "balance": "982846000000000" + }, + "aaedb3fa2cf0ebca0ef4a121a28a406264ccc900": { + "balance": "100000000000000000000000" + }, + "aaf30bf76362a03450aefaf5bd68d28b84eb4962": { + "balance": "509106199370000000000" + }, + "aafbaaa6b6369e986ba72b196bd5f08cc458e344": { + "balance": "216372214000000000" + }, + "abb03c888d61c9102827a1dc0950145beb9d96b3": { + "balance": "144000000000000000000" + }, + "abc6dc937d7703a6b0c83659a328cde0d5008e32": { + "balance": "4052429106341000000000" + }, + "abd3910139a97cb92dc09a8a0352575bcc9ebed3": { + "balance": "24028359215749000000000" + }, + "abdc3953ef293c98989802063f8cb55e0e506432": { + "balance": "64000000000000000000" + }, + "abf1a47c582bc87d36e47cfce24e0ad249f42e73": { + "balance": "71947491720909000000000" + }, + "ac0b6e7aadfb5ffafd5cb3ef3620ebb0691cc3fe": { + "balance": "10000000000000000000000" + }, + "ac1a182607046b56e7a4bbab87cc1182874f79ef": { + "balance": "453499500178000000000" + }, + "ac251b311f781ad7a43d01b0b4b20fe891004e7e": { + "balance": "304621378298000000000" + }, + "ac258cec5ef49f96612d659f66dd4e6ea88e3c87": { + "balance": "255185373455000000000" + }, + "ac4000d9ad080740ef4a2ebe4a3075877bea277e": { + "balance": "10000000000000000000000" + }, + "ac7445c09372f15259fd852cc458783d6140c0db": { + "balance": "10000000000000000000000" + }, + "ac8d29dc05ea6c2f5409a76abe04321bf9381f32": { + "balance": "22464474197854000000000" + }, + "accd52b63822d8cb5117d9deb56596e072462614": { + "balance": "20000000000000000000000" + }, + "ace63a86a2ddfc79f677344e93dc0c4750b8fdcf": { + "balance": "1355066360964000000000" + }, + "ace83deb83fa8d319979658592b75ed13bdf97c7": { + "balance": "20000000000000000000000" + }, + "acf91515df16b21f1e5f5474dbefe596e4929b96": { + "balance": "1153047238967000000000" + }, + "ad04381f7ba89220e8fcd7e200f98a476683a904": { + "balance": "2000000000000000000" + }, + "ad22225bf225d8f705f93bdcda8d301180ea28dd": { + "balance": "1272512717188000000000" + }, + "ad3f74034ff5ca89f97b2585edf12376820307ab": { + "balance": "12303261515593000000000" + }, + "ad43a3527ad2b9445417cb73cbcb42965a5f469c": { + "balance": "67607364133000000000" + }, + "ad61cf9bf560bd5da75d55738477bd9aa25fb0b8": { + "balance": "4358939446693000000000" + }, + "ad649e8a3e1436e0604b0b8c9b1a5f1c09e06d7c": { + "balance": "344000000000000000000" + }, + "ad6b584813814db4c72c4c7eb31447d224074b46": { + "balance": "18445595367000000000" + }, + "ad7d404afc67c0e457fd3ce142cd30b506408683": { + "balance": "48218702840000000000" + }, + "adaf4d39b6806d132128ac438c2862c0a1650cff": { + "balance": "500000000000000000000000" + }, + "adda124baed2e1fdc1acc7b4a048eab0cd249212": { + "balance": "1074765673925000000000" + }, + "adef437c429d90a350b99750d4b72bc8538c5f98": { + "balance": "931901903135000000000" + }, + "adf826a0ea7dc4322d26e9d8c54c4180c1827216": { + "balance": "323567723315099000000000" + }, + "ae01d8b1668f8bfe6e225bd9bc746f7e839ac0d8": { + "balance": "321211880744000000000" + }, + "ae17de3ae6127022e53ebcf9e08457174cdee0e9": { + "balance": "3817903000000000" + }, + "ae243b0186793eddc6ebbb1a2c1f0b1cd574b07c": { + "balance": "9000000000000000000" + }, + "ae3ae1d41dfb16e19a1817b3639cd4300fd166c1": { + "balance": "55437674845679000000000" + }, + "ae506999882d4c6f05cc7979c342c0ce559a8df0": { + "balance": "1391755905401000000000" + }, + "ae524cee5aa23025d6ad185ccab75a6974335d53": { + "balance": "797132751509000000000" + }, + "ae5a55075d0541f179b085152bfc8c72c74abe23": { + "balance": "589408139567000000000" + }, + "ae63d02b18b464f0bbab4de943766bdc7ba2926d": { + "balance": "300261019201000000000" + }, + "aed8ffb86a49c09ae3a83e93d9045851434a9f0c": { + "balance": "1031991707237000000000" + }, + "aee18a9a2ccdf6025d61005827753ce4f510f7e8": { + "balance": "1818639022863000000000" + }, + "aee67910c514fa63a228769d5e15ca40bc4b26c2": { + "balance": "5688989238568000000000" + }, + "aef744eb2ec682dca128dc3149afcf881e367121": { + "balance": "818801643225000000000" + }, + "af04430b3e40e746127623532353a0f177a88fe3": { + "balance": "100000000000000000000000" + }, + "af181833edb15c9b2ee2329dcf1845b977361b7d": { + "balance": "93228805338000000000" + }, + "af30db29765b4fda6f075af96e8acd5046b099c4": { + "balance": "1000000000000000000" + }, + "af31fd30cfb10f1b0a12c2e7dd7ca56bdf517745": { + "balance": "36000000000000000000" + }, + "af70d6820e1d26194b0a9965b55254a287b162f3": { + "balance": "87593999609754000000000" + }, + "af96a573fa86c07389a71db797bea689419b23ca": { + "balance": "36000000000000000000" + }, + "afa4c5b674934e31a9aa5e3e723d85060d51c4d0": { + "balance": "10000000000000000000000" + }, + "afa6e4b4060c2e5969c2329d13cc42924412efde": { + "balance": "127502378589556000000000" + }, + "aff2308ac607f85392f4c8a6a043af67b7b849cd": { + "balance": "11130371831000000000" + }, + "b00ea9c459105b650def1e8569c85fa01837454d": { + "balance": "94928352162000000000" + }, + "b02a7d16ea8663c88416e6f64eaf57787d230be3": { + "balance": "17215604601000000000" + }, + "b03f4e9aa5c352cb1cec953d1123c2f22cd94b5b": { + "balance": "206022552274000000000" + }, + "b051459b91d253c5e8251a5a68282c291833466a": { + "balance": "297127749975000000000" + }, + "b055bdc874ca5a7d2f4bcbc51f1cfc3671b55f72": { + "balance": "1421913523478000000000" + }, + "b06156b99b891b756262c5b40db9bbe39fddc77f": { + "balance": "49000000000000000000" + }, + "b076893b9841d2775ec8883f05b74f1e5aec327c": { + "balance": "22591055478000000000" + }, + "b095de644af3c9f960f67502da6ac5eb050a158e": { + "balance": "4958067562725000000000" + }, + "b0a1f794cf70422395f74395abc9a7d0b271846c": { + "balance": "812057322000000000" + }, + "b0d36e0f426a99416425689c657fc6d64ad698ca": { + "balance": "1157727077158000000000" + }, + "b0f35fa554d6ed657bf3996cc027d045c3971fcc": { + "balance": "64000000000000000000" + }, + "b0f76b4c9afdfe35c41d588265642da60f1b97d1": { + "balance": "1000000000000000000000000" + }, + "b0f76b4c9afdfe35c41d588265b42da60f1b97d1": { + "balance": "2028311808491377000000000" + }, + "b1445842d56c56bc532d2f33ab9b93509c732a3b": { + "balance": "13522982470164000000000" + }, + "b156bafe05973bc839c4f115be847bbde8a67cb1": { + "balance": "10000000000000000000000" + }, + "b182e4d318893dc1c4c585195dbde52a84ed4ffd": { + "balance": "329498977335000000000" + }, + "b18f506e77df4db80ca57cefeaca4f1010f78f50": { + "balance": "956339304078000000000" + }, + "b1b6f617b110dd79c8fd77e729584d1fdfa9aa09": { + "balance": "16000000000000000000" + }, + "b1bba36e2d9e272e0131f4bae09bcfd92e0a63db": { + "balance": "64000000000000000000" + }, + "b2285651e57ae0ff27c619207cceacd20884d152": { + "balance": "1345938295122000000000" + }, + "b2419a93732d0d324daf7707fac3782a77b0dff8": { + "balance": "625000000000000000000" + }, + "b27206e9f2ac430841fb8da69b49d505f1558b8b": { + "balance": "29507819229000000000" + }, + "b2801fe902c7bbc987ba12ecae98765c99980fef": { + "balance": "240016083000000000" + }, + "b2843d5215ceb761e78f281402a1660c3abadf5b": { + "balance": "3335539720927000000000" + }, + "b2a22e6a04a2ce3287da3b8b6eed4ea1f18f05dd": { + "balance": "99999978999999999999" + }, + "b2d55a061fc6f90d2a05e0cbd26ffe0a1c3321c2": { + "balance": "1000000000000000000" + }, + "b326aec1cd523948ffec2fd1e8f21bd2b4308f40": { + "balance": "913000000000000000" + }, + "b32abc82b251e2d310ea7588cae4ad4acb657cd9": { + "balance": "26946233911000000000" + }, + "b36924d578973aec05ce7ab556d7ed00004949ca": { + "balance": "393041705867000000000" + }, + "b37482114c83e857c730588d7d959d300b8142da": { + "balance": "29429544454000000000" + }, + "b39998bade135ac6ccadff41cd709e161d01aa60": { + "balance": "26272579375000000000" + }, + "b3a995ee94f1d63d12f10cea5ab3d596c7c6f284": { + "balance": "64000000000000000000" + }, + "b3bf35e936fdbb7d0bbeeb1cf076f243855ed477": { + "balance": "754081187934000000000" + }, + "b3c2ac85b99abed4a2a52b5f96a2c98040d16022": { + "balance": "50000000000000000000000" + }, + "b3d1a2c0ab2d8987446d74f49e357adf5bf15986": { + "balance": "10000000000000000000000" + }, + "b3fbcd24c8394a5d2b7fe877f18681a109a404e5": { + "balance": "2558689648423000000000" + }, + "b4110f4e38405adfc054e55ff73c55842db8e2cd": { + "balance": "129000000000000000000" + }, + "b417f4681fdd4e53cfdf8550e3d326dbb0a557ec": { + "balance": "1000000000000000000" + }, + "b422970fb8799d83642b7ff715fc941d69e86053": { + "balance": "81000000000000000000" + }, + "b4237be71920497715826eae8d85c26cb3c111a8": { + "balance": "10499979000000000000" + }, + "b431839de4b21dfb44150cfc6ed00ea430a81687": { + "balance": "26839560174813000000000" + }, + "b43a0d6399c7d1be943c4b45838156a47c88f909": { + "balance": "10000000000000000000" + }, + "b44ec608b95d0d51105ce5f4b48de5dd72f346fd": { + "balance": "448125120000000000" + }, + "b47f63e14f6de6c3413b2be95a725e367ac18fb6": { + "balance": "500000000000000000000000" + }, + "b48071cd1b15f45028e9dec2237f14f10b7aedf9": { + "balance": "38042711385000000000" + }, + "b4b874b323b560aa0e4811ca574bd48b65b3fc72": { + "balance": "18063913676592000000000" + }, + "b4e4d4af0667f8158cf817bf1bc3eada08a551ca": { + "balance": "2149067370317000000000" + }, + "b4ecd625ffe470ee1fa1d97832e42ddf3f9ddf6a": { + "balance": "1181738860120000000000" + }, + "b53f380ce92787c1db461524290e8fcede552fe7": { + "balance": "12640674931821000000000" + }, + "b547e04ab8a44d3cae38704356f1f59408457b67": { + "balance": "286604155735000000000" + }, + "b562e4010a0a5fd0906a4cd9f47fc28f6f51e210": { + "balance": "1000000000000000000000000" + }, + "b584de7b38a2a2e3d9ff9c055b309ca56e5da5a9": { + "balance": "237896887904000000000" + }, + "b5c1129961c4a43673324aaedb8296f5ade82516": { + "balance": "4213058948283000000000" + }, + "b5da6711c72bf27c87923aed4a39349b4192e6b4": { + "balance": "55180742586465000000000" + }, + "b5eac5e7e03b9d31e40393e16e956cd588cb7566": { + "balance": "4508019435556000000000" + }, + "b5fd46ee4e02946dca3485439f98bdab290c82b7": { + "balance": "108321600045000000000" + }, + "b5ff2a3caef6ec30365f4f0ecbecbdeec1cacbba": { + "balance": "979696597242000000000" + }, + "b609d05242f7c13a4ae4036f6da9c0bae18dd70c": { + "balance": "229121731278000000000" + }, + "b611156a2f87fb45c431a5cf5740ded90c2dc542": { + "balance": "401783365700000000000" + }, + "b61c7623144afbd0f6cf44c951e4219ef8096119": { + "balance": "36000000000000000000" + }, + "b61cbe0e58ff6fa4c810ad03c759c79d9ff052a5": { + "balance": "1034495371371000000000" + }, + "b622bb67e95a03f58dc9aecf82c217e86f2cf7c3": { + "balance": "500000000000000000000000" + }, + "b62a50be3ce0e7cf8f61991daf8fa7e23775141e": { + "balance": "1000000000000000000" + }, + "b63cbff6b1747ad5cda101d5f919ce81dd67e363": { + "balance": "2570089937000000000000" + }, + "b65e80551a8687c9cef2d852949177c0e3b56e51": { + "balance": "100000000000000000000" + }, + "b68126ebbcb5ab9b0371b62597a38d5c1685b0df": { + "balance": "671140851028000000000" + }, + "b69f5830c371cad5a74ae823eb8892d153ef3c23": { + "balance": "18446744063709551616" + }, + "b6b4468c4db64e0b85cddc251d02f32fffcd1f7c": { + "balance": "10308006217291000000000" + }, + "b6c129312505e571148dbe69833d30550efc12c9": { + "balance": "5105834767567000000000" + }, + "b6cee8ef00b8674a9a96447e4511b30d6564ff67": { + "balance": "667754569888000000000" + }, + "b70f805aeba260d44f0730f0a9dec60f2b4f54a1": { + "balance": "2751303297000000000" + }, + "b71a901dc4b6c6463f7d221f868677bcadbcc680": { + "balance": "169000000000000000000" + }, + "b7385bd8f8257331f4c7a87c7a23724f615cff8e": { + "balance": "196000000000000000000" + }, + "b755692bc027e30730dc1d0e0b2a883830a84115": { + "balance": "30713083153428000000000" + }, + "b765305dda3c1e069a7a022ec127ff2140d0a820": { + "balance": "603122990932000000000" + }, + "b77403a4c56ffc7715b4bfdfe4b054336aeca466": { + "balance": "130840969728386000000000" + }, + "b78b2f6dc731d7d84b7eea151805f9208a1d0cf0": { + "balance": "142084687500000000000" + }, + "b792a0fd762c002a7585cfdefd36cf7ffb42fc05": { + "balance": "10000000000000000000000" + }, + "b7ccd7164aa7fb871726d9d043a8f8f890068c0f": { + "balance": "1170997140237000000000" + }, + "b801f49018317caf30f310dbe116f4e876184874": { + "balance": "50000000000000000000000" + }, + "b81ca2bc63cb4008cebdda3ce8f4eaba322efca6": { + "balance": "4678481047354000000000" + }, + "b82e3d50bf8c5b471c525ec8dd37b06688ed6178": { + "balance": "1202448975553000000000" + }, + "b841162a7a8876296f10794d8847d8095426aa54": { + "balance": "73500210754000000000" + }, + "b8421d375c3f954e22b6fd304235dd7c43b68bd0": { + "balance": "6499782706009000000000" + }, + "b859b76d77eb604728093c61fcabe6f9d22433b0": { + "balance": "196000000000000000000" + }, + "b86536268ace9be93a1db2012d6e3e59023ef2cb": { + "balance": "52878034904067000000000" + }, + "b87e1ac4fc423ab37e10ffd221df8056537b1d03": { + "balance": "119159824674000000000" + }, + "b8825a99806c5a968423e69d22f2b61a2f0ae9e4": { + "balance": "999999999580000000000000" + }, + "b8835acaf63e0e5d41fb743eb0f954040a38d381": { + "balance": "64000000000000000000" + }, + "b8844c74b227781d4b3fafd32e39ff6fa9857f77": { + "balance": "490694157000000000" + }, + "b8962e8bcbcf0f69144f8fcd2ec3ae8e54c05034": { + "balance": "1425313342735000000000" + }, + "b898b4ece8e0eea375f6eb85615652cc5c221593": { + "balance": "2284038029169000000000" + }, + "b8a949bfd9751c29c4cd547cca2e584d8dac4e12": { + "balance": "50000000000000000000000" + }, + "b8ad5ce2ae781e2d245919c15bbbc992185e5ada": { + "balance": "733786526623000000000" + }, + "b8cb6a9bc5a52b9cfce26869e627b2af0ff5ed4a": { + "balance": "98364826821577000000000" + }, + "b8cf6aac7b9028649f0d55a57b61640d70cef120": { + "balance": "104799890645000000000" + }, + "b8e827b5d1e10a3944039adb1a3dd7ff6949145c": { + "balance": "172413427060000000000" + }, + "b8f6d7f33ee5755ba56647ab8fc9ca27b8aba677": { + "balance": "1430769696978000000000" + }, + "b9221177e2b09725bc95f08c72c17c42887eea62": { + "balance": "1212779749827000000000" + }, + "b936e0d83cde9bb810b85ad58eb5ff0fa9c11654": { + "balance": "4999580000000000000" + }, + "b961d435c457e205fdbed5442c8614ecfd59616c": { + "balance": "27847452621284000000000" + }, + "b969e9d89f32002cd4f90ef5907bebbbdca6fe6a": { + "balance": "12455448454838000000000" + }, + "b981c9137cfca5389f0123927852278d2d7ff618": { + "balance": "92180707865000000000" + }, + "b98abf0fe91b0d3a16c6ed37aea446baea33fd23": { + "balance": "560454425563614000000000" + }, + "b99ab4e6ae277b9fb04537adbb781e8390b490ad": { + "balance": "32814665223319000000000" + }, + "b99d0a433d7994743dd675894c18ed03164436e1": { + "balance": "16000000000000000000" + }, + "b9d8b6f0a505d217709bb9327f3b9b3f84813e00": { + "balance": "81000000000000000000" + }, + "b9dbd64e3c8e6ad84c9c67c66e678c06ea7bcb91": { + "balance": "1161140466507000000000" + }, + "ba361f7a6dff16a96f957c63e08267dec8f9ecf7": { + "balance": "2170060167590000000000" + }, + "ba47f4136f74b566f62ba373651332b59e74e1db": { + "balance": "906249296535000000000" + }, + "ba5287cf15de91daeaea2465da4d4c1a14dea716": { + "balance": "98978398162000000000" + }, + "ba77d056d52f84e740579aa527792f826591c858": { + "balance": "50000000000000000000000" + }, + "ba895406774ced5fd2e759b58f9ffaed5e04fb14": { + "balance": "10000000000000000000000" + }, + "ba96fab21a4926fd1137558ae996b52ec14538a6": { + "balance": "10000000000000000000000" + }, + "baacc247801eddbf152fd6ec39d659f265935743": { + "balance": "2661902597584000000000" + }, + "bab2eb9fab8e699a958699b15ddc7ada5428d33a": { + "balance": "27006404153000000000" + }, + "babeacd7933c817472875c86bf126e6d11886f8c": { + "balance": "2461234517292000000000" + }, + "baf7021d4d754d4478d3c3624c2376e3f1d4ee5e": { + "balance": "1352066301857000000000" + }, + "bb0760bd1da973d8f70dd0caa6cadfcfd8199231": { + "balance": "177674700430000000000" + }, + "bb278c6a52eebd0b8950e9b78ba211453ccb1b6a": { + "balance": "25000000000000000000" + }, + "bb327e5f260b2dfe25fb180c2d3f4b63211c1dee": { + "balance": "7694972715000000000" + }, + "bb643e768ab20c135e7df3f400284cf04c40a6f7": { + "balance": "385756449779000000000" + }, + "bb73d1d1c289b4953d0033b52d9d2d0d92573d22": { + "balance": "11000000000000000000" + }, + "bb89936d562b19e4c599826ce7cd0c60cb02b512": { + "balance": "725910446589000000000" + }, + "bbc509b7999b0e94534477b98ec8927cba879677": { + "balance": "20000000000000000000000" + }, + "bbcfa9ab62f4eab14d6a1b09c1aa554dae113183": { + "balance": "589417352665000000000" + }, + "bbe78301134249b52b74d73ee3855e7e3d288a40": { + "balance": "4456159000000000" + }, + "bbe7bb4c4f1b506b58f7e3334e6c89011cf2d6a7": { + "balance": "3889127030000000000" + }, + "bc016690596e077273465d1728d18553b185654c": { + "balance": "185932953686000000000" + }, + "bc16b2ab9c7ab309249f93b496b75c6a7392cb10": { + "balance": "5000000000000000000" + }, + "bc254e5405b154b98abb5fe5508d3e7c98663f4e": { + "balance": "144000000000000000000" + }, + "bc258aeb0f18150d3ca253c6bb04f63d657d99ac": { + "balance": "6011905701701000000000" + }, + "bc2620b5ebac12a88b287b625fa5b336568e7869": { + "balance": "534886892259000000000" + }, + "bc318687cfaae2be4c5ece4a18bb9252486a19d0": { + "balance": "147226513970000000000" + }, + "bc32dd123fcc2ef0dc36484c3ab1bae5d9890761": { + "balance": "16000000000000000000" + }, + "bc5c5151be06aaf6180bc9c1058b181a5a30366e": { + "balance": "113865120384000000000" + }, + "bc66241ca430dc31a3e2f44dedba868e16b9a6a1": { + "balance": "50000000000000000000000" + }, + "bc7c371af0688b1c409f4b07662609a1c9efd120": { + "balance": "20000000000000000000000" + }, + "bc9454f7efc86e25d18a8e8b6e230de42a51d967": { + "balance": "148103676062000000000" + }, + "bc9d5456b975bf0b95c161c3355e4ceb28898fd9": { + "balance": "28083912047000000000" + }, + "bce0b47bf13e4517c53bbbe6e51544b99f3147f6": { + "balance": "919711480389000000000" + }, + "bce2d1ec7c41b426f72b352f5f2b7da3edac4157": { + "balance": "908085365725000000000" + }, + "bcf0756789a57f16206dd78bf6e1322ba9b9b85b": { + "balance": "110888224252000000000" + }, + "bd0bc4a0730f9f55a2f65f62662c7553db52238e": { + "balance": "8440290043000000000" + }, + "bd29fda37c2581a3f040c77eead3143cff24a346": { + "balance": "126022762542000000000" + }, + "bd4c1270322a26a1b825040b239008a447c31918": { + "balance": "727012140904000000000" + }, + "bd6a3da2db66dc9fa26fa2b63b14003d26ef91d0": { + "balance": "5492112771780000000000" + }, + "bd80fcccac60078fcf09f5bddd8a25a92fb9cfdf": { + "balance": "10000000000000000000" + }, + "bd92dc94b6e81a3da5dc3ae6bd80782622658196": { + "balance": "10000000000000000000000" + }, + "bdb35c2c595fe7a2864ebe20dd56d6ddaf9d447c": { + "balance": "4346566125000000000" + }, + "bded4718cbad2150c9b6df9ee7356e0f5c713cea": { + "balance": "311694803600000000000" + }, + "be1804630ecd95ac411b935566cecc5a24c6f18a": { + "balance": "85033246331000000000" + }, + "be2318ad50b0a85b95870a81dce5c31029636159": { + "balance": "5185298019030000000000" + }, + "be3de52fc1119f02f4707f353c040b7c4222d847": { + "balance": "25267399461000000000" + }, + "be4feae01d429c872601ae84dfae8fddc3372686": { + "balance": "20000000000000000000000" + }, + "be7c09d704d16e4b2c9e19cc8c07808bb335f926": { + "balance": "25000000000000000000" + }, + "be873a9525899bdad5e4376b0115950e534dea2f": { + "balance": "404116929377000000000" + }, + "be891b1680ad835aab1ac05a30c0813306cf20f2": { + "balance": "144000000000000000000" + }, + "be8ed2d85a5e3f83c6105db1a1f304e9f174bfce": { + "balance": "50000000000000000000" + }, + "beb1cd80c2f8fabc27ee3a3b2a15e35fa52e7879": { + "balance": "11539095431000000000" + }, + "beb67375e46950830906bf281209be133075452f": { + "balance": "1305262446956000000000" + }, + "bebe54437722c6000bc6a8843f159538a2abf613": { + "balance": "41042548942568000000000" + }, + "bef2a05e283ae948efa9b0e3a6ab5d26a57f1de0": { + "balance": "180614450853000000000" + }, + "bf03950f265a4182b4402703723a0311158eef4f": { + "balance": "158997402149000000000" + }, + "bf06393654baa1ad15c2e717e06dbaa61834c214": { + "balance": "34409427774000000000" + }, + "bf2b867313a44bd04aceaa644771d1e95317c881": { + "balance": "10000000000000000000000" + }, + "bf350ccad91a2a2aff4cf27a291323a297a78009": { + "balance": "124593326152000000000" + }, + "bf3d86edfcf52733e91a9c59be606a95bd921885": { + "balance": "20000000000000000000000" + }, + "bf5b21d5e339752b33b180064d0e6047338650a5": { + "balance": "1000000000000000000" + }, + "bf64c2715db8f353600a45b9264e1f22a40ef8c1": { + "balance": "2952972677360000000000" + }, + "bfaff32c8b04a61658ff94f94e4687232b8d2d7a": { + "balance": "1117691379350000000000" + }, + "bfb00182321502e0729d9a0862ec1df1b3e2208e": { + "balance": "500000000000000000000000" + }, + "bfcdfc9f60610f0ca279ca2c89b9af831332aece": { + "balance": "1431082635308000000000" + }, + "bfe14356e86f6b2ad470bc77d250517c8dc03d15": { + "balance": "310115085185000000000" + }, + "c008bd3fb881da9dca4cadcc56b1d99c56db9abd": { + "balance": "12899598792000000000" + }, + "c01efea456d30360a78ee10c790d46bcb889ee61": { + "balance": "103203021492000000000" + }, + "c03d622627bba7d5db1a9f699924e9d5ff5640f2": { + "balance": "95102233308870000000000" + }, + "c0465ed806ce7ee730e5b6eb7b86a754bfd196a9": { + "balance": "1654379359619000000000" + }, + "c061c5b0d0ce7af95ded1805abb23f743e13c455": { + "balance": "500000000000000000000000" + }, + "c074f2024f79cf8d7aab2d858dd110fc2ee89d41": { + "balance": "18382732686000000000" + }, + "c085147a76d0336b4bd6e7d5b60d394bfd3c6f42": { + "balance": "3236912707535000000000" + }, + "c089416d2d679cb2abf44251de227d0a08fa1206": { + "balance": "497124416350000000000" + }, + "c09d8cfd85989397dc723f2df821dbfb2c0c39b3": { + "balance": "833485701262000000000" + }, + "c0aaf130e3b67250d9775d62e7cd3963daf0a627": { + "balance": "1249947125780000000000" + }, + "c0aebdb5c2e8c5ff9870535c738bfe892c9365dd": { + "balance": "360097616959000000000" + }, + "c0db5680ba88052652bfd5a617c4e8a5be188077": { + "balance": "509051625766000000000" + }, + "c0ee350e5e09a2daeff332a66a6e117fad102112": { + "balance": "10000000000000000000000" + }, + "c12c0a3fd42501f8772e4ad5d262eef3f0bc4701": { + "balance": "120398848512531000000000" + }, + "c135b48c7fd11670bbfba923b28767d21d7923ea": { + "balance": "20000000000000000000" + }, + "c1397c66b7f150c0062b0e87c981c107d771b109": { + "balance": "87751498250000000000" + }, + "c1507ee435cf506fc5d8e4cb62515f2ea0f3a7ae": { + "balance": "4935384099000000000" + }, + "c150d185e2cf203054a6e328b72d8c35bfbbcc33": { + "balance": "21044148271000000000" + }, + "c163098f8b8f0736862274860b3842cf14bd2288": { + "balance": "119025568966000000000" + }, + "c1687fbbc7d504b73fe3e71af440b3dec0da88b2": { + "balance": "229520711528000000000" + }, + "c172bf224080d448261b3b66453074b28628daf7": { + "balance": "7903438287958000000000" + }, + "c18e9bc05dfee2a39fe2b6778a24a48d5bf0f141": { + "balance": "500000000000000000000000" + }, + "c198fec4069c95300d34b9c7109d7441b8e62745": { + "balance": "50000000000000000" + }, + "c1b4134f4757d19a687d05bd7087872b5625405f": { + "balance": "20000000000000000000000" + }, + "c1b43ca2af534ac6bcad8f23c30c07ba07e7e8fb": { + "balance": "194999622000000000000" + }, + "c1c2249507d2dcaf4a9103fcea2cfb47aa4957f7": { + "balance": "571416394325000000000" + }, + "c1e90af40fb64427aeb79a13607debbae9270b52": { + "balance": "50000000000000000000" + }, + "c1ffc8938f3412d19d428b8450f17fd394ae539a": { + "balance": "36000000000000000000" + }, + "c20013e25ae53d0d41bf365aa767822bbbe70936": { + "balance": "10000000000000000000000" + }, + "c20e9eadffa5529ce58a39f5898f39906dcd4b78": { + "balance": "757301065305000000000" + }, + "c211fc2623d51846d26952628d140643efa5156c": { + "balance": "865384323985000000000" + }, + "c2546c312570b30ad2ed05edb13b6469494c5b92": { + "balance": "5000000000000000000" + }, + "c25b2280ed0f835538f8ffd9dfc08a3b853f1ccf": { + "balance": "1000000000000000000" + }, + "c260e43b89a7a4e84bcc4c21dc43d4b5e6923f3a": { + "balance": "1000000000000000000" + }, + "c26aeef0e1f382c88bbdb1eb8c01afa7f58218ce": { + "balance": "79774757760000000000" + }, + "c27dd2645254bc30b6cf7bf418803b02ac808b5e": { + "balance": "4419594173874000000000" + }, + "c2b4f6cf92d6d63a20034e409a358df1803159b8": { + "balance": "1630820442000000000" + }, + "c2ba4a7ea6ca2d17231fb17ebd5dd2dfc0964de4": { + "balance": "221662324727000000000" + }, + "c2bc18f24b8097208a8b2418c444ea58beb94281": { + "balance": "1766754009521000000000" + }, + "c2c028dd17f8a89b9131b7daaeae9cb1dddf86e7": { + "balance": "10000000000000000000000" + }, + "c2ed78a0cb850c12ce8e6ff3873e8c18ffc9f4b9": { + "balance": "1017518755567000000000" + }, + "c2fd7296210b7013d476205d2517d51b21c9e76c": { + "balance": "500000000000000000000000" + }, + "c3041d3d650ff6ac3e35b60371b6798360727651": { + "balance": "1011071365226000000000" + }, + "c328ab9ce1fddd5623e0383828714a7e3ff12eff": { + "balance": "285042661579000000000" + }, + "c34ab008ddddf376dd866cccae4a4d6eb88403e2": { + "balance": "2798642711076000000000" + }, + "c3511391c4515cf8f27e9bc0f777a02a4125c8b1": { + "balance": "20000000000000000000000" + }, + "c36916a9fdf656bb1a8c2f7fb752a3489020f6ff": { + "balance": "689483152953000000000" + }, + "c37598a388d6f4e8e046923265ee9256456e40ab": { + "balance": "62865106394696000000000" + }, + "c38813db256eb221a7142d042b81ba2babab2c31": { + "balance": "98477603778000000000" + }, + "c3acd30f0bc3146fc2cab8d54904f98289021374": { + "balance": "17820000000000000000" + }, + "c3ede34dc1cd995fda1c5cb6e9ffd0c0da080587": { + "balance": "1080428143758000000000" + }, + "c3f04dffe2be55a1d6cdaa78e5c09a79d0477e7b": { + "balance": "59747493842929000000000" + }, + "c3f09f681cfb57d3cabc547dc32a71d2a6585c1a": { + "balance": "1757648436173000000000" + }, + "c3f3bb6444d853614f18c04a3c81f7d26e62e96a": { + "balance": "9022830778000000000" + }, + "c3fe4534327a2fc4144e2d3d3392f7b78d2aabc5": { + "balance": "1759225739027000000000" + }, + "c424f5be9490ec7f0f1e2debc3f72bd83e35f587": { + "balance": "1774372626989000000000" + }, + "c434f64eb937207f80e9a02d2f77ca34bfc63aa2": { + "balance": "960850858644000000000" + }, + "c438b6fa5801a4b8dea450530d975f174cdd47ef": { + "balance": "64000000000000000000" + }, + "c446effb984ff3e5ed92280e7b3dcdb1284230b3": { + "balance": "503490303680000000000" + }, + "c453ae9f94253ebdb871e9dac19056b13d1747a3": { + "balance": "1621494076559000000000" + }, + "c4a473b5e3a6bfb51f963d4dcf109bddedf4fb43": { + "balance": "104273242373000000000" + }, + "c4b8058e9e5416e526ea16e37f29dc221d28a003": { + "balance": "1833513486496000000000" + }, + "c4c09f4bbae0ee06f2a52ff0ef0de1978b5305e9": { + "balance": "20000000000000000000000" + }, + "c4c5981f5ac0a9a3701663b887c4aaac3a3a4d1d": { + "balance": "1411640000000000" + }, + "c4f7a493d16aab4d18e88e530e75e3095a3439ee": { + "balance": "191606419322000000000" + }, + "c5259c18bbd8b0485ca83d069d5ac235b28f24ea": { + "balance": "1276479076242000000000" + }, + "c526ef1124c7d0549b117e7b7463539a24209290": { + "balance": "9106523141000000000" + }, + "c5278b9eeff2221604f30f002c307ca2882fba97": { + "balance": "20875716591000000000" + }, + "c527ca73562846de9fca1649fe5144e5068a2f6e": { + "balance": "25000000000000000000" + }, + "c52a960c5df55169ed5d5cb0109a576321ab82fa": { + "balance": "1097338876493000000000" + }, + "c533ab799e5a04e0ba4e4780d632e0044262d216": { + "balance": "200529941482000000000" + }, + "c5389e3ee2f043ac2b6481f254440a97a9cf3bdb": { + "balance": "84047554571000000000" + }, + "c5594292b324c1d63f797c588a589c895c680ed0": { + "balance": "334298857161000000000" + }, + "c55d7ae4f29d857182d5f1ac2a78cbf35a694dc2": { + "balance": "500000000000000000000000" + }, + "c55ead0ece8fcfbecc573666c0170228e089aefb": { + "balance": "438775082956000000000" + }, + "c55f7d73491cdba391b631581029de32755a09b8": { + "balance": "1340000000000000000" + }, + "c56cb4e8308d6462eded0bbc74965ee135e23e11": { + "balance": "568187503785000000000" + }, + "c5b0c5f840f579536d5977a77262458d72ef1490": { + "balance": "5880686297881000000000" + }, + "c5b129c764daac8bfbf023646b9306d817a8ebdd": { + "balance": "10000000000000000000000" + }, + "c5bba43db949e2ed3de3036caf7a6e42558b1ef2": { + "balance": "763947031151000000000" + }, + "c5d57171e5b9cbafaba7d2c13cca3ec9d81bda49": { + "balance": "25000000000000000000" + }, + "c604e6c539c857ae9e60ca20d1906308ba431892": { + "balance": "100000000000000000000" + }, + "c607bdc5ad2f189e9356edb4d7975c7ba9300836": { + "balance": "55828814399000000000" + }, + "c60b0d2341ecada6c3faf1efcc9027125d99e17a": { + "balance": "121000000000000000000" + }, + "c61e1b993c3fd91a1023ba5b92d06a0aa539d92c": { + "balance": "23863993763643000000000" + }, + "c624656ee5298786cb3d0de045b0ac089c5341d6": { + "balance": "2210389938000000000" + }, + "c6573a023d6f4b5e151f266af4ec0045df0d1518": { + "balance": "52505006485983000000000" + }, + "c66b1d84c42018b16dbc4777409bf50a49febba9": { + "balance": "9078953000000000" + }, + "c69e4de93457f251b1e0879b5250b26e57839fec": { + "balance": "500000000000000000000000" + }, + "c6c51205c9f0bcaea05dce8e47e91d94a3f63c2a": { + "balance": "2720612321571000000000" + }, + "c6d237e0936c4714e701823aadb368fdc471451d": { + "balance": "541700595551000000000" + }, + "c6dcac15739872089cb3d23287e8cd546487ecf2": { + "balance": "1023857245227000000000" + }, + "c6f40b81a5860dece34305f53570be61cdf9a8fa": { + "balance": "20000000000000000000000" + }, + "c7147a95cc4f6bedce6292e8f95539caf550e9d6": { + "balance": "20000000000000000000000" + }, + "c7185b1a680d8b0893065d8213de54375d086420": { + "balance": "11564622085000000000" + }, + "c71b3876613c928197aadf3dd7888db3665f28f0": { + "balance": "112276274428000000000" + }, + "c72200bb380db62a3fd741713d332be77bc1a4ed": { + "balance": "6962060809000000000" + }, + "c7345cd5a7eafc9d7ebdc17d674f83e23336538c": { + "balance": "4425703195684000000000" + }, + "c734f9dc3ee2d857ac826b101129eb77a4a22256": { + "balance": "100000000000000000000" + }, + "c736fa9550b73f4a4ca0ac1cd94bf6f42ccbb11b": { + "balance": "449139000000000000" + }, + "c74128ea37f5d1ee016086a38e470bb332eb5270": { + "balance": "40479951869000000000" + }, + "c7647ec91e823cfe57e8a3433ddafd7b4f675b80": { + "balance": "307102062000000000000" + }, + "c76d49334ce25f5fc62841e5a87d4e03ab3edd9f": { + "balance": "109999979000000000000" + }, + "c771093ed5c4df518536b76e013e8142ecc3f9ed": { + "balance": "5247752820195000000000" + }, + "c780dfb4cdcba4dc89245a8be8a93de1a3e82d3c": { + "balance": "205580199482642000000000" + }, + "c79c6c3a0a46052f723a26b1f107a332474df3a1": { + "balance": "50370325181000000000" + }, + "c7a4e02d2c0f00fa56662cc9f323cabeff82759f": { + "balance": "1163435680762000000000" + }, + "c7c0632cff11812130c30163c83746839a625f95": { + "balance": "10000000000000000000000" + }, + "c82238664bedfa8ded51e91969a39f13a8262a37": { + "balance": "10000000000000000000000" + }, + "c877d228c350ec0d8d97802e7d874d3130171813": { + "balance": "199845203467946000000000" + }, + "c88b8a2e498fee366a1290a575a7f09da12ea8b2": { + "balance": "50895598476000000000" + }, + "c8bbd0e52b11ae6a20adc5f6bbe4d34d7440e8ca": { + "balance": "114566193776000000000" + }, + "c8ca2bd1bef02b505f0333996bcb6bf730648390": { + "balance": "1177250974576000000000" + }, + "c92c3358910418fdb3950e1a378af7246553ae38": { + "balance": "81000000000000000000" + }, + "c9325c9b6d2af226bc5ae1cc975e00cc11274cd1": { + "balance": "2927587698197000000000" + }, + "c95ae8dbc8bb075e7dcb2b2c6d9411bedf26244e": { + "balance": "931878010706000000000" + }, + "c98fc33c1d980052d75fee8b34d08796734b6a4d": { + "balance": "8671327034000000000" + }, + "c99fba8321d16cb19c55703b407c54ed106dcdc4": { + "balance": "20000000000000000000000" + }, + "c9a0da2a3be799e751738e61b9cc376eb06e2b00": { + "balance": "50000000000000000000000" + }, + "c9afc551058c32e89bc2d6704d0d00e92f5ef6d7": { + "balance": "11135553563900000000000" + }, + "c9bfa2ad4b3e9c624255c6ede116421b04487d65": { + "balance": "105514983171000000000" + }, + "c9e4b61d8ddeee339e31ba088efb5d608c3464a5": { + "balance": "20000000000000000000000" + }, + "c9e9090d9f95f401c87c7240f3bf88ca9b540f8b": { + "balance": "553735838243000000000" + }, + "c9fd40bb35284e3d7f0dd3b43a1d9e958f7c86e0": { + "balance": "50480449695128000000000" + }, + "ca038c7c9e66531ad79e4d67b42d7920b7f05c26": { + "balance": "64000000000000000000" + }, + "ca0d08f6019884f94f2b5b191ac9bb247150cd13": { + "balance": "25078089364984000000000" + }, + "ca2c6e6ed3d6a1d030807f91e1fd5c79d36af86f": { + "balance": "849454139892000000000" + }, + "ca7c7bbc24cac0f3aabfdccc77df21004672e634": { + "balance": "6952718700000000000" + }, + "ca998c74383b55c8dcddd46b49f95456fb056b7a": { + "balance": "2000000000000000000000" + }, + "caa989e6a1e934532aaae6cad282c18b1a0b9fd6": { + "balance": "2335540529729000000000" + }, + "cab32ee5cce74e0ee88bbd4b505aa587ef2e4bbf": { + "balance": "75914058971000000000" + }, + "cabe9f0d0a18de8d3495dd063b04c6a33584a8c1": { + "balance": "116083536145000000000" + }, + "cacde94daeafc06e46c86b1e20387a23d909ace8": { + "balance": "1521003430346000000000" + }, + "cafbad01b81ad6cc401883773994a9dd6e6ed913": { + "balance": "10000000000000000000" + }, + "cb343b882cfe866f73cd5f0f31fc68cebaddd882": { + "balance": "221801563082000000000" + }, + "cb3a7aa2e97517b6ea8d9ed0ac270a6a9cc6e079": { + "balance": "958830201738000000000" + }, + "cbd2c4916211ab2c234bc8a51e6f680b59aff782": { + "balance": "24279462419000000000" + }, + "cbea4ed5e8d2ffad442e482fa5f8d551ef2a58e6": { + "balance": "26730000000000000000" + }, + "cc001ce4f4417505116486bed9fdf04bf97ca246": { + "balance": "31740534557000000000" + }, + "cc0b53b26b6dee9f8226f25b834085bde13f5eb5": { + "balance": "132440104515963000000000" + }, + "cc174862456f02f349303d1b8328495de8ccd789": { + "balance": "155951512603000000000" + }, + "cc2af3921727d6d2de31d5f656f837a5475de6cf": { + "balance": "10000000000000000000000" + }, + "cc3201749f55f0d7b450110bc11f65b1ce165d2a": { + "balance": "123428947550000000000" + }, + "cc3f37ad6b449e39c544e26bbdf4d7be66b9dab0": { + "balance": "348574664284000000000" + }, + "cc5b36c9ecea12ebfd0721a58ac11b0c340a3f44": { + "balance": "384197170701000000000" + }, + "cc5b410c7797faa05ac4233eb31b468ee4bf279f": { + "balance": "10000000000000000" + }, + "cc60b223554cc6425374c5e2424df7007621368a": { + "balance": "1128118098000000000" + }, + "cc7027381d98c2e883c82bb9c2f85b985e1e7b4c": { + "balance": "1370000000000000000000" + }, + "cca378f16e07258b9c15921233110fb4729645d2": { + "balance": "151974946930000000000" + }, + "cca781d996c3ef985bf7d2b4d68d55f52efe1905": { + "balance": "2217463190039000000000" + }, + "ccd0b9f6ffb0383553c355c6a14be1200966d47d": { + "balance": "12917165349191000000000" + }, + "ccfa4594129bbb9d07cb4ae8dc2b1c8f3bf98508": { + "balance": "524845286088000000000" + }, + "cd19c879df458106d179bbb5b7f44609d68e6e5f": { + "balance": "8601633489844000000000" + }, + "cd1c55037a0570e8f9aaa95ef157ae81a1969250": { + "balance": "10000000000000000000" + }, + "cd1e47695b0fc93b82cffd0326852dc04d8441f0": { + "balance": "144000000000000000000" + }, + "cd1f90c388d76b3aeaf77850f2191f12a2311f51": { + "balance": "1728456799866000000000" + }, + "cd3aecd58de07f80b64044875fa6ad4f18f72789": { + "balance": "2648597880142000000000" + }, + "cd4f39123ece1e0ab52cfa2a5d059b49c4d63c3f": { + "balance": "1661718859439000000000" + }, + "cd6ed2f7ab49515f8fd70aeb4d72bfae8956b5f1": { + "balance": "183807926254000000000" + }, + "cd9d9d07fcf476a8ee7240324a602449606d75f4": { + "balance": "100000000000000000000000" + }, + "cda66d375a10a22f13dff8a9c40b63461daddab3": { + "balance": "1116940051064000000000" + }, + "cdb0832ee5b26da24b1775c4cf0dfd669b94ce00": { + "balance": "23919219542965000000000" + }, + "cdba5805f17df1f3e47647464de978944ed36b62": { + "balance": "4204539000000000" + }, + "cdd1df8bd54941e26ea26eebbd537e751f64f5f7": { + "balance": "5000000000000000000000" + }, + "cddf5b34342200c37ba96eb0dd662ca4c29f89f8": { + "balance": "10000000000000000000000" + }, + "cdf6c838980afd91a600e3fff755a4848d138568": { + "balance": "25000000000000000000" + }, + "cdf7f55a5a16572d2f2bbf7faeffe3c4d64f86ab": { + "balance": "3115969322502000000000" + }, + "ce0f1dbbfa3490a21ee4b28232db612f44bb7bf1": { + "balance": "9227310122000000000" + }, + "ce33184573c33dd859450304984fa63ea4f2b62d": { + "balance": "7055925237496000000000" + }, + "ce33a3db107f01c51d30b24a8db80faf05308bb7": { + "balance": "10996113113089000000000" + }, + "ce4922b3daef62914f0580a55c524e6a02e31d83": { + "balance": "5541295938315000000000" + }, + "ce4ce8a8540678dda16380c211482dd8c8b71092": { + "balance": "6224176337062000000000" + }, + "ce62cfd71abb9979a0acc398c17dbb5cb6da4721": { + "balance": "13448605175000000000" + }, + "ce724bb30c7821a9c847e0a3e9c12843c3471f9d": { + "balance": "252657175031000000000" + }, + "ce8af01494c2c5b4e74bb02dc6de982e7234fed2": { + "balance": "77349533545000000000" + }, + "ce8c774b7f92045faec43e9cc1711224a3b32435": { + "balance": "370287579971000000000" + }, + "ce8c9ed5018559f36ec72e5a9b0701724e498b51": { + "balance": "142866501748000000000" + }, + "ce995c13568a8b1521d4c9721cfc11da4891860b": { + "balance": "1000000000000000000" + }, + "ceab9dddc767a9651e98527fcf51f6e85c9ae402": { + "balance": "5251411770975000000000" + }, + "ceace25f8c7cf853500a461df007f9c9703ac4a5": { + "balance": "1428847332255000000000" + }, + "ceb0c49dad36f6169ec82a2f0d80da36c87e4209": { + "balance": "459821324064000000000" + }, + "cee8083233bcb4d50ddbf2121c90b5c2019ca58d": { + "balance": "557985245088000000000" + }, + "cf0c6bcc66eb75899bc7f8ed4b8d2b29437bfe85": { + "balance": "3252418478000000000" + }, + "cf32c5bf1d7ef0cb0f2f190f8468b01a4f2d93e2": { + "balance": "6593164924646000000000" + }, + "cf6e47463382153fcf0ec6738880925dbc08116a": { + "balance": "1091910654350000000000" + }, + "cf7539096fd0cd97cd316efcfe9d3c87a101a74c": { + "balance": "741847588809000000000" + }, + "cf9439bf2fbab65cecd783e135a37127f585f1e5": { + "balance": "50100000000000000000" + }, + "cf9bdc902604fab070c611ebf6a989ac4a785c82": { + "balance": "1501000000000000000000" + }, + "cfbbefc0e6013fa2caeabc54ac05f45dbf17ca13": { + "balance": "230809632301000000000" + }, + "cfd53f18ac7d94cadd032a0f4cdbdffaf4765d6e": { + "balance": "64000000000000000000" + }, + "cfe66dc4aa9ac9c9f87fdd05c1b2b95da5211703": { + "balance": "1656993051100000000000" + }, + "cff376eef4d69c4a47d6c7916583228fab3b5967": { + "balance": "5904462494391000000000" + }, + "cfffcb819302d05ed763026bdf84b48818938fb0": { + "balance": "289619807900000000000" + }, + "d000aa72a77d55911a5e66c2906da9206db86633": { + "balance": "3008989624945000000000" + }, + "d02d7b42213e873f91e789cbaffc734ffabd1087": { + "balance": "144960809826000000000" + }, + "d02db5279e918b3e93ff81d00d4025cc71dccaf6": { + "balance": "2386625717975000000000" + }, + "d0802cbcca2bb516f251b873eb20bb5e94af7f37": { + "balance": "9287997718210000000000" + }, + "d0c07380308972a36f57d1cd9081d7389d0421cb": { + "balance": "1280367167470000000000" + }, + "d0c131c1b60891b91e58fbed787ee4567e3f2038": { + "balance": "6360752089492000000000" + }, + "d0c71159d46c4d2af7699f682a055c79a1a68a0d": { + "balance": "1527974433762000000000" + }, + "d0d5d9f242f2613079b3b443c359c2e18ed5faab": { + "balance": "637334647476000000000" + }, + "d0dd208ce92da02eee3ee3de335e67f819581a33": { + "balance": "100000000000000000000" + }, + "d0e55ec0ad0f8986dd9fa9d738007c5bdc22f840": { + "balance": "53012893797000000000" + }, + "d0f222cec657ee444e284c07228d585155b82c0a": { + "balance": "7368748129592000000000" + }, + "d11efb07887d8b5b87a77d8fd388190614e8c077": { + "balance": "4703283503278000000000" + }, + "d129f1b89045ebfb4d1df1d9077e9359fd2990f7": { + "balance": "14496053137000000000" + }, + "d15a509424c4e04868bdcf59cbee09882ba04c8d": { + "balance": "65042393236903000000000" + }, + "d162416912b03fa65f3972a63e357ceaa3b621f7": { + "balance": "325650177224000000000" + }, + "d166183164b81bd049b2146a3ccfcc78cc6a0bdd": { + "balance": "1000000000000000000" + }, + "d173d759f0916e61400d56ca690cbf1743fe27b0": { + "balance": "53550838679000000000" + }, + "d18dc883e3881bf4c7db2afaa097bc2d33656724": { + "balance": "5000000000000000000" + }, + "d19dc9b5ae689dea1ccbfea8b44ec6034559e326": { + "balance": "135552499885000000000" + }, + "d1c79160d0b8c1a1546b86db5123e87645a45d13": { + "balance": "10000000000000000000000" + }, + "d1cccaa22259c547993df3c147d5b545f003adb8": { + "balance": "10000000000000000000000" + }, + "d209c9f32f3292ac4d15ef353fbe6f6efcd4e49d": { + "balance": "81000000000000000000" + }, + "d21ac89a20d67e309f96f64adf05fc48f55918a9": { + "balance": "500000000000000000000000" + }, + "d21f6e7adbf480600295af683091f9b9833f5330": { + "balance": "1229445878922000000000" + }, + "d22700a47a0edb137d2f0348aa0f8d4b6dbc5850": { + "balance": "21301422923000000000" + }, + "d258ddc9372e3b70ff53da171252239655ca9886": { + "balance": "16000000000000000000" + }, + "d274c69317dd836df48562455e8f5a7bd2e47d19": { + "balance": "156091832558000000000" + }, + "d286b68a358fcf8a6cec70b83467079664632ae9": { + "balance": "90377010699000000000" + }, + "d29284915d9b924ae5673e8a4a557478f68a7471": { + "balance": "324678197320000000000" + }, + "d297e64ac2bd8e98e6d276d6fe080679c398a26a": { + "balance": "3401930527000000000" + }, + "d2a1e7b51f6b5930a0d9e2ee55736f3d83a1b323": { + "balance": "44578900750000000000" + }, + "d2c9b0b0bbe61de504e4f210c168fa5999c9c23d": { + "balance": "76537483113000000000" + }, + "d2d49f650d222ec3e2cecba163ee92f0e934ca14": { + "balance": "3312486482635000000000" + }, + "d2d803bf10ba18adef5716b4056c1b1d61c45abf": { + "balance": "964679698000000000" + }, + "d2f673b589df7ef5cb32fdeef842d48d66130567": { + "balance": "1079010447581000000000" + }, + "d2ffaceef1af3f1c3e3f35e4062cd9f9abd1da59": { + "balance": "3041453068594000000000" + }, + "d30a74f5041ec6e73d066a375a105116699ce177": { + "balance": "21814020745000000000" + }, + "d30d849a2d8ff5041304014ecf6752dc769bf004": { + "balance": "1247532881540000000000" + }, + "d3113f558c6376321691931c9b21205e31f4a56e": { + "balance": "572224428451000000000" + }, + "d314bac1bf85eedeac0b359dd2106dbae8fc6947": { + "balance": "20000000000000000000000" + }, + "d3283e17112028b324327ef64a238183ba189207": { + "balance": "136000000000000000000" + }, + "d33ce3c3b64d1b3d399651432c15ecb943d16c70": { + "balance": "10000000000000000000000" + }, + "d33e1e4b10a98e82810f6d161df5d35e5677e35f": { + "balance": "10169656674000000000" + }, + "d34699fd152fe38caacd3c096f6abb1cd79e88b2": { + "balance": "25056644550000000000" + }, + "d369c0e01b9a9d519b3b099a98fead19867c019c": { + "balance": "100000000000000000000000" + }, + "d388dcfe55a9b710d05c686f033fdbdd7861ab71": { + "balance": "1439589263065000000000" + }, + "d391a7d45c7b454b743bd867f8f62f56894f9b65": { + "balance": "484904747488000000000" + }, + "d39a75b4831543e1bc99e7a5ca8875c4f69da45b": { + "balance": "10000000000000000000000" + }, + "d39ed6978b6a90fea29e735f8ea3f1d20e0fbd15": { + "balance": "144000000000000000000" + }, + "d3a0a1a00dcbd6bc44c9803ce351a4b36a69c929": { + "balance": "191222401916000000000" + }, + "d3bf1c0a6b0470c30fc49d995025af5e6b639e61": { + "balance": "10000000000000000000000" + }, + "d3cda762bafaf204469f85e6896ec64147a3452c": { + "balance": "468094119213000000000" + }, + "d3d04d78c1ab9e6887a9467b8b1e31b5c9910e5c": { + "balance": "81000000000000000000" + }, + "d3e1bfdd9396aba00d3e78646ddcdaf139a967c0": { + "balance": "833333174120000000000" + }, + "d3e502c42ff0274da12ba87ffd45fa593bba052a": { + "balance": "100409899947269000000000" + }, + "d3e76066c2e32d9a693161de07f2d3b7e6ea07eb": { + "balance": "10000000000000000000000" + }, + "d3e8d577323d97407246b198c4c61f7943c468cd": { + "balance": "10000000000000000000000" + }, + "d3fd4d1b0edbc314b103d350fff023ab75b7d7cd": { + "balance": "84129547428000000000" + }, + "d40087fca8feb72d130bbc9622575d4987f12895": { + "balance": "1000000000000000000" + }, + "d407d4126cbf3619a422c532ccf20c3da1495dbd": { + "balance": "99622000000000000" + }, + "d41a28761c8e5de8c803813667f1dc0918a105be": { + "balance": "157507410260000000000" + }, + "d46ed38228a3c3d78065b2d8b71b325bf0f0e685": { + "balance": "6787045850000000000" + }, + "d4a7463d202e804b39a93bccd77491d8791baf58": { + "balance": "171694163573000000000" + }, + "d4c20716ff7288d811d05fd6f0696a9f5627a11d": { + "balance": "100000000000000000000" + }, + "d4d95059c808cf41e64f7f353246ffae635419d4": { + "balance": "10000000000000000000000" + }, + "d4ef925157c6d0e2d649332f44416b85f8abe69e": { + "balance": "1392945162611000000000" + }, + "d4f0cb25801794f6d803306878763e08209d19f4": { + "balance": "64000000000000000000" + }, + "d55fbebc4dcf2de6341c2325448e9c198f0f06a3": { + "balance": "14206622892000000000" + }, + "d566968c40211fb25114105e36b5a7219cde9d5f": { + "balance": "4898442964000000000" + }, + "d5817b95c6b504a6d07f64faccc9aedf408b0ac4": { + "balance": "54387832478000000000" + }, + "d59679fc40a71897065bf1b3a73f331226cdae72": { + "balance": "20000000000000000000000" + }, + "d5a7deec4a5898f094e1600f9b15768d8aada258": { + "balance": "100000000000000000000000" + }, + "d5b91c29bf772ad3ba04033dfb86b672b245ad77": { + "balance": "100500000000000000000" + }, + "d5c1a9bcc5e68b7547354178fefb3d870572fd67": { + "balance": "2252066779089000000000" + }, + "d5da2a826f5909a221bfd8561dbd7dbf4aca4c35": { + "balance": "13839784966766000000000" + }, + "d5dcc82fa169b4677a3fc26d78f38e27dcc763f3": { + "balance": "10000000000000000000000" + }, + "d5f344ee8a1b954ae5fd8fc7ac702174749bc8a4": { + "balance": "1398836216771000000000" + }, + "d61cd03afbfc1bea186e5a3a51347c2c4ee3a2c3": { + "balance": "109879472702000000000" + }, + "d647fd7ca17203a0049c28ec6759612d767cfcce": { + "balance": "162681136487000000000" + }, + "d656d14acfb2f0fbde2ed2a137a52d852bb6288b": { + "balance": "20000000000000000000000" + }, + "d68130b421b19c193d03a9017b2dc687c7307d26": { + "balance": "128569735484000000000" + }, + "d69a41f7ca76b40ee94b0d04a3780a00c6c651ba": { + "balance": "2801054372864000000000" + }, + "d69af2a796a737a103f12d2f0bcc563a13900e6f": { + "balance": "7412286547000000000" + }, + "d6a5a7e149cbccb72a50b0a3ae00e6756b0a7eda": { + "balance": "1075352201657000000000" + }, + "d6aa9957f141f0dfed77e943c39aeed978834fdf": { + "balance": "20920740110000000000" + }, + "d6e99ccb72d24e8a60f24d47afd4074b1d1fd336": { + "balance": "15415994387186000000000" + }, + "d6ea4a9dda8d5bc832229c916fa45f05f99c093a": { + "balance": "27075799893190000000000" + }, + "d71de419d746ac277baa955761cced4b34c376ec": { + "balance": "1388473506822000000000" + }, + "d71ec6b5e5d4c604f741bafde0974eca49c56156": { + "balance": "61938809628000000000" + }, + "d72f90d9879f6d2d407b4fdf5d128b98d518f1a5": { + "balance": "10000000000000000000000" + }, + "d743d7925a0cfd08150814cce8cd5d3f7099e1c9": { + "balance": "25681376856000000000" + }, + "d7575a09e7498f21cda3e9e7266b7fde91dfe19b": { + "balance": "9841565066000000000" + }, + "d75c2eb5e0a2b2ee72ef4fa7249c1a1ce03f333d": { + "balance": "134491489751000000000" + }, + "d77088329ec1e280ea7a087ad20c5e965721ff4d": { + "balance": "3949070941222000000000" + }, + "d78d564bb79ea19e4a93975a38fe0882018f177c": { + "balance": "992717434142000000000" + }, + "d78efb176b252ce67b5648e04088d12c4668aad1": { + "balance": "10070463674000000000" + }, + "d7a25e43d7d4e23744f0b10e2b4f2911fd3b3bc1": { + "balance": "1000000000000000000" + }, + "d7a941cd82f8aa63c55baa81db44bcb347b8e529": { + "balance": "49000000000000000000" + }, + "d7b34387880daede6cbdad11bb3db67daf942975": { + "balance": "20000000000000000000000" + }, + "d7bca0770e2f890c1e93c3595641241454a31045": { + "balance": "2000000000000000000" + }, + "d7cb675cea1c0dafded44f611c9c344e2a5e053c": { + "balance": "25000000000000000000" + }, + "d7e53a2d8eaefd18e02bbadb7e64906ca8613151": { + "balance": "166599594268000000000" + }, + "d8076b9db0b7496efbd198b73c4bfcf51ac080fd": { + "balance": "210272077089000000000" + }, + "d81dcf5756da397ff1f783ffe5391d1ffd4ff227": { + "balance": "500000000000000000000000" + }, + "d833c6d08f5fff8f77628ab1e86584d052976d1f": { + "balance": "10000000000000000000000" + }, + "d835732e85953baf2af9e49f770bac1caa1dac23": { + "balance": "152211441541000000000" + }, + "d84005fea447e8c6aa0b5436ad79654a75348456": { + "balance": "22563694224690000000000" + }, + "d84d9c59a445911922e88c0f22cc6534f33ca3de": { + "balance": "3054115413381000000000" + }, + "d84e69926216065749e624d87783e90ce3015b82": { + "balance": "1420803164313000000000" + }, + "d884bdbdb7e13cc523e7f192310230c7bdbb4a07": { + "balance": "10000000000000000000000" + }, + "d88eedca1dd9249702f5ffc807c1e439eee1c5e5": { + "balance": "36000000000000000000" + }, + "d8a23fd234bada1c726622925ade62d3021e0037": { + "balance": "1567046607931000000000" + }, + "d8b1aee24264efebd1c677fcab6ada6e0f000cc5": { + "balance": "20000000000000000000000" + }, + "d8ba7afbb8bf2910b983a114aedec626eb7426c1": { + "balance": "275491152435000000000" + }, + "d8c8af55ebf116ba3c3904f8ac39d3a7d31aadc5": { + "balance": "1499999998278000000000000" + }, + "d8d97645f5f62aa89bf0046362dd0f45d40f821f": { + "balance": "25000000000000000000" + }, + "d8ea0e24a7e28285c4454f54181d581324da2583": { + "balance": "53039425000000000" + }, + "d8f5d258164747ccf790f5ed358162c756de49db": { + "balance": "323009990690000000000" + }, + "d9477bb62d3eb668a83a9679f3a7ef43f17c9e4d": { + "balance": "14045557127869000000000" + }, + "d9585e1b03fc86636dde1e64aed3cad77868549a": { + "balance": "1000000000000000000000" + }, + "d975f9ce3fe773fac3f8338a034a757c58f6e11f": { + "balance": "25000000000000000000" + }, + "d97e490faf19de612fb49c041d3f9e7877d3c0bb": { + "balance": "65766847746000000000" + }, + "d98117b74b2f2888d7078d3116d5758e2d09bfca": { + "balance": "1749157484422000000000" + }, + "d990b3f69ec700bdc095c184b3804551c832d612": { + "balance": "509385034698000000000" + }, + "d99b35298e709e5f54e6a5c612a326a83f4268c4": { + "balance": "71963571266000000000" + }, + "d99d9ec76005da26ccc721ec26be4ed9b3b1c586": { + "balance": "469607736824000000000" + }, + "d9bc61075c3201351584a026e5bdfb7cf9a7b6ab": { + "balance": "200000000000000000000" + }, + "d9d07b72f83491b6db26602f6b7039aeebfe6b61": { + "balance": "144000000000000000000" + }, + "d9f6f1ddf03e836b3744d008b62a6424544c67a5": { + "balance": "74347470143000000000" + }, + "d9fceff07ad69bf3b4aef54a7eee541368980cf6": { + "balance": "1143407707495000000000" + }, + "da0285fb7e37fd4be66fb862b248cea94ea8f6db": { + "balance": "80770216661309000000000" + }, + "da05c7aa330fcc5834e19deeb0a808e9ab7f3d99": { + "balance": "169000000000000000000" + }, + "da129e4481bd25450e6c7b42fe417c87ee2ce7a7": { + "balance": "256000000000000000000" + }, + "da32e3ec421993db088c71e256263158f7855b61": { + "balance": "18540215888567000000000" + }, + "da3c99669acd202ccbe6f80902c807588eca0880": { + "balance": "1000000000000000" + }, + "da72a7bec114d43aee6449db830d2d3f16e4d9b6": { + "balance": "744534872932000000000" + }, + "da72ec2cd7b8e3924f8baaea75d5ed23ef39394c": { + "balance": "38646377617204000000000" + }, + "da73078957f491827d62cb3ca0c484c2d1004ba7": { + "balance": "891774109242000000000" + }, + "da828e50c7c8580c6ce81718f11fbd43b2b0541f": { + "balance": "66094097819000000000" + }, + "da91483db6a6a034e068e69a6b46674838c5bc80": { + "balance": "4000000000000000000000" + }, + "da9551b635c3619f81641571e267755b89f7fe1e": { + "balance": "670841942250000000000" + }, + "da9b43a9c1c574580ec43da9f6acb687fc2f8c68": { + "balance": "761695404114000000000" + }, + "daa7f446923f7481115ad285ca468c865147e563": { + "balance": "10000000000000000000000" + }, + "dac6bfd15954efa4c9254e24e5831ab1884f8d67": { + "balance": "960042043423000000000" + }, + "dad85d0b8bb5ebf6ef811d0d35c89f9f343c833c": { + "balance": "37664599958479000000000" + }, + "dad9b01652de5d50bf30f9bcb0c6edc6315139e3": { + "balance": "21500996724000000000" + }, + "dae9c7d6bb0efe3f7ea20442b184f6d99b2a2c12": { + "balance": "937830189066000000000" + }, + "db0d6c28e9b913f611accaab15cc887f9b770f58": { + "balance": "20000000000000000000000" + }, + "db0e5341d64817885721c5abff04c30bd38df40f": { + "balance": "62600679700000000000" + }, + "db387dd404d14478babb60bad2391720d68b92ed": { + "balance": "115096708329000000000" + }, + "db3fb19e8a4a6ace4d8c6c02085d4cbba528b532": { + "balance": "1000000000000000000" + }, + "db4f05a66c0ccf0532ea1ecb931e05a400a6f4a7": { + "balance": "20000000000000000000000" + }, + "db571f1cdf8e83fbff6fb48cc0c81ef95ede12a0": { + "balance": "118317387393000000000" + }, + "db6a6a6db2aef3f43afbfe23027b670ebb3d33bf": { + "balance": "9960755220000000000" + }, + "db8bed34f8f34f45cb83ab19ed33fad76437d217": { + "balance": "21003651205000000000" + }, + "dbeba6a2a7f66c20c6db7b9270a5aee74de3f441": { + "balance": "4086905723945000000000" + }, + "dc04efeac13b2dab3d07833a7e7fa728fc23d18a": { + "balance": "1161834099120000000000" + }, + "dc092386c3a3e28b6b2d7d70db8f3d11e79ef5df": { + "balance": "124362293793000000000" + }, + "dc10be66aa11acbd42a2b1953714f09b5281681b": { + "balance": "20000000000000000000000" + }, + "dc2d58a383ce0bd40bed859ec2f25412b68eca0a": { + "balance": "104917823922000000000" + }, + "dc2e38183dceb2bc82b23e8ccf48dd96ea1c97b6": { + "balance": "2847787376064000000000" + }, + "dc5d9d94530d88451cf081fe7f2ac33667af9d8f": { + "balance": "65321904051000000000000" + }, + "dc993112a8d89136e0e73d67e2f26191583a50ec": { + "balance": "1000000000000000000" + }, + "dc9bb69b295945589a41feb794406558ce65dedc": { + "balance": "104077637254454000000000" + }, + "dcb0109d4fca2dace08ddca5d989a09d470161a0": { + "balance": "28897833222000000000" + }, + "dcce3a4ec516d833f5a9790c40ad0334b0d2dd01": { + "balance": "25000000000000000000" + }, + "dcdb21cc811ab733c2a80a2d5c8e5bb49cb2ddc4": { + "balance": "16000000000000000000" + }, + "dcdf8dd3ce03a2fe0d72835dbbd58725e1ed2c57": { + "balance": "113330414284000000000" + }, + "dd2165839ab95d6b24591307adcde9ee1819927a": { + "balance": "20260199589072000000000" + }, + "dd3015a5fdef66e749a000585d5574f975e3432d": { + "balance": "85465001652000000000" + }, + "dd37c478727f44943c5fd79ace30f21fae5a589a": { + "balance": "108577233510000000000" + }, + "dd3fb5810c31d37652bd17b92497ed479faf123d": { + "balance": "669996966072000000000" + }, + "ddc3bda30f7cf36bd535de4e20c9becb78d159f4": { + "balance": "99998278000000000000" + }, + "ddcce2d2431b67d4157c7ac4bd77f20c24831de6": { + "balance": "36160893899000000000" + }, + "dddcebe609f8b4354a1f27ab1915135e25800344": { + "balance": "1457846339959000000000" + }, + "dde223eb48f81748abde6cbc08cf1e6b0e8e4e5a": { + "balance": "1501921107087000000000" + }, + "ddf4ba402007060d9940a96f8e7c39f0a2c6108a": { + "balance": "377268151287000000000" + }, + "de05d9ada6626a8492acd137c7c7f7080a987cd1": { + "balance": "222144234923000000000" + }, + "de0fab89f79c4edf9766c3b7b1f508cb43c5495e": { + "balance": "8278000000000000" + }, + "de1070f3ff6c47237768fbdead88b2d5184fbe2f": { + "balance": "1000000000000000000" + }, + "de2b16d7f36f630329287822f550ec19415acb3a": { + "balance": "25000000000000000000" + }, + "de3faf6884337a99e54ac5d3f08b34be51b0755b": { + "balance": "51926806905184000000000" + }, + "de4614fd632ddac888d177de0858e62bbbf7dc11": { + "balance": "52506376589000000000" + }, + "de54cabb241dc5def530191f948f67db942a85b0": { + "balance": "9691177060000000000" + }, + "de81e488284acc4f8f6061d3a73bad112efa7a40": { + "balance": "14654060992000000000" + }, + "dea86ac3a661272691c877c1bad8355789382b69": { + "balance": "903877103000000000" + }, + "deadfc79f2a722dbf1c1a92f2824da8874189fea": { + "balance": "98905944986000000000" + }, + "decf1af47e153f6f3749a1b7abadefdcf1607a0f": { + "balance": "529000000000000000000" + }, + "dede5fa984f0def639d5b633f54c60fc5aaa272a": { + "balance": "8193771708654000000000" + }, + "df23607c63b3fd4b5fde6aab093c0c56d1188f95": { + "balance": "14687379916000000000" + }, + "df5b74bf02902e4c466821de798406b663d4d73e": { + "balance": "9000000000000000000" + }, + "df6402ee3f37389e7f65720561b54e26e5f1cbaf": { + "balance": "358266132937000000000" + }, + "df83ea5b770d5abeccac7f0cae803e8bd7b9831d": { + "balance": "25000000000000000000" + }, + "df92802ffe9892c7704875755bdec648914430e6": { + "balance": "20000000000000000000000" + }, + "dfa108bcd80e824a255679a38b2450d428e2f939": { + "balance": "489209553654000000000" + }, + "dfa9f18e859353796afe384d05353dc80b3ffc43": { + "balance": "121000000000000000000" + }, + "dfb0d6580f011e68a39d7727818b0890e70f3036": { + "balance": "537675412560000000000" + }, + "dfdf006abf2293aadc58feea6af6b35db428675e": { + "balance": "9000000000000000000" + }, + "dfdf2ba93bd47d7243b7419413458a947effcf67": { + "balance": "45080282196110000000000" + }, + "dff01277ac23a8cf93383595a80a7c070eafe5c6": { + "balance": "312778552103000000000" + }, + "e0450154c441e52c5e507e8316d4e9376c59c12b": { + "balance": "170163401434000000000" + }, + "e059374d6a7e6c63e609b65642272869fa3b2b3c": { + "balance": "300122497803000000000" + }, + "e0c0d8e739a2f274a43f019a07f7f61d7d8e11a7": { + "balance": "2630310240000000000" + }, + "e0e779e4e3573ea77096daec252ac2b3f1c0013a": { + "balance": "10000000000000000000000" + }, + "e1325eb586180a67873718a2016172afeb03c6a5": { + "balance": "531691399657000000000" + }, + "e13958af480da6443b9ec1067f0f33440634a282": { + "balance": "10000000000000000000000" + }, + "e142daac753b2c4d215372797999e9c88b65dfc9": { + "balance": "585813299366000000000" + }, + "e142ea343bc36ec49989fd43ad5c403c70a40dbe": { + "balance": "656734902975000000000" + }, + "e14d0a3d259db6bfec2fc4ef6e18729e4d93b007": { + "balance": "210234446279000000000" + }, + "e16a5316e3a113f27bafdf3d4fe44fe30ae9c210": { + "balance": "16000000000000000000" + }, + "e1770944aec145a96c9491497eacf7f3fb03c1b2": { + "balance": "335417250470000000000" + }, + "e199ac237661dcac0a4cfab404876abde72ee209": { + "balance": "340000000000000000000" + }, + "e1ad427471023f38cbdf07fdca3728ec343810c4": { + "balance": "343957267368000000000" + }, + "e1ae0223cecd738c8e530a0007ef05e8f3b33769": { + "balance": "950528515289000000000" + }, + "e1d2d4ef39f01a60c3bb5d671af91c5298d87711": { + "balance": "121000000000000000000" + }, + "e1d4a8888cbb383f3671ca96e7b55310b59a2541": { + "balance": "242387826125000000000" + }, + "e1da9039ddfe117e6a0b484fd3962426c112871c": { + "balance": "3710499693813000000000" + }, + "e1da9f16d57c601af8b6d102323c20408af8531a": { + "balance": "3135322588771000000000" + }, + "e1e1c163f391ffad2d0be68641253b0860485a95": { + "balance": "10000000000000000000000" + }, + "e1ec6361df67ad915df9e9661cd0932186db034a": { + "balance": "4279936304854000000000" + }, + "e224050bcd723e63f1fc0567a86942546aaf8d13": { + "balance": "12007628539000000000" + }, + "e2342c7f411d7ca3a86484af59a9c3f3180e2f0f": { + "balance": "16000000000000000000" + }, + "e26f2b026a258ce5801c90bb8fd6f7a152b8d267": { + "balance": "304593714834000000000" + }, + "e2717eb5fd0a1da51272b50ca8d12858009c7016": { + "balance": "506943817535000000000" + }, + "e27546d5620e6398829260e58e8cf4a3a03f4164": { + "balance": "3000000000000000000000" + }, + "e2762bb64e0606a5d635032e15164b01f612a74f": { + "balance": "884716158811000000000" + }, + "e2882066ed0a3c041d09c00c8532850fc42eac06": { + "balance": "42441412728970000000000" + }, + "e294c5d64daf7b7c0994aa9d03669c4b2658c9cf": { + "balance": "6996693830997000000000" + }, + "e2b179f0ed6870a6268aea64b0c7b39d98d97fcf": { + "balance": "334205318353000000000" + }, + "e2d1f6f7e3128340b789565b527bb91de96d54bf": { + "balance": "100000000000000000000" + }, + "e2f136d3693aa0b2346a968a22aca6707fc1d0e5": { + "balance": "10000000000000000000000" + }, + "e2f229054293e32cf3e83f9bb88d9cf1d6acd66b": { + "balance": "20000000000000000000000" + }, + "e33b8f4c9a49554c8b134861f88c8fffc399e456": { + "balance": "83552502198000000000" + }, + "e33cfc7727b1460324b34277dde14cc49bcb273d": { + "balance": "100000000000000000" + }, + "e36af9bfed4f912cae21f3d899f7354e1c902601": { + "balance": "31474316356000000000" + }, + "e36eff7c061dec48446d47675f176b4da3c2e950": { + "balance": "10000000000000000000000" + }, + "e383f3cf431f3cf645f26c7d5e5e2f77348ede6f": { + "balance": "776224304171000000000" + }, + "e398b9f004a4f891cf871a57d9124a97b56e89e9": { + "balance": "84846187740000000000" + }, + "e39ab2415144b46db522e92ed51b8089a5ec01fd": { + "balance": "4158925896363000000000" + }, + "e3aa7ac7e15e9a8a6f54565067234a9d4bf7b569": { + "balance": "1080385576951000000000" + }, + "e3ec5ebd3e822c972d802a0ee4e0ec080b8237ba": { + "balance": "2129139289000000000" + }, + "e3f1fbff8686af23ab95eeeee6a6a03782d72416": { + "balance": "401776848194000000000" + }, + "e4001b830fbd86df257ebab54aec0c66314ef9aa": { + "balance": "518220809325000000000" + }, + "e40790bff894f0b3e534942b5ad6f6592cd6e896": { + "balance": "25000000000000000000" + }, + "e409170a296e46fc96d85a2395e4324212a470ee": { + "balance": "1072528749756000000000" + }, + "e41546f68bbe1771febbdac2a4a5999eef50edf3": { + "balance": "1000000000000000000000000" + }, + "e425d63d711a9996c09d928ba8df94c88163aea9": { + "balance": "10000000000000000000000" + }, + "e4432ff1aee13f97f73a8407e4c7d6e768b8040b": { + "balance": "700508995102000000000" + }, + "e4690f5d024a395355a7cb5238fb7e0dc921b1e8": { + "balance": "1000000000000000000000000" + }, + "e4829684fb36f054766a61fb2a8f6ecdf27c9e87": { + "balance": "73885178137000000000" + }, + "e48a68e1ac007e14ac08c1b3b0df2b5602081ec2": { + "balance": "1389262869176000000000" + }, + "e4d699b3f4117eba7ed27b323048c9ffcb46ed42": { + "balance": "183131036697000000000" + }, + "e4db688c29fdf9a1c16114f99797d8409545955f": { + "balance": "16000000000000000000" + }, + "e535b94d370190d1e0955d3c0d12480e558f00dd": { + "balance": "20000000000000000000000" + }, + "e53966d4bb17fa9b50d29b44ddf3951c9ca67caa": { + "balance": "6400630678000000000" + }, + "e56f2656fdd1a5f7d3716e65dd89a37dd6e42dcc": { + "balance": "1000000000000000000" + }, + "e5a364113076273352e0c31bf505028e0b7edbaa": { + "balance": "10000000000000000000000" + }, + "e5a3c80518fab6a0a721ccbdc3e673680a65f6de": { + "balance": "171727917465000000000" + }, + "e5c71c7170e5c9b07e62cc307d81a4a3053ed64c": { + "balance": "10000000000000000000000" + }, + "e5fb6408db128c55cfb3e7fa1942d6347e34932c": { + "balance": "10000000000000000000" + }, + "e606883236f8b2045393c574153a100675cd4b90": { + "balance": "14005226900000000000" + }, + "e61869d1cf72f25e195898217f5bf5bcec9c9038": { + "balance": "50000000000000000000000" + }, + "e61e2e29c0719457ab1bf7d6d9fe442bd6107b07": { + "balance": "30943034333100000000000" + }, + "e61eb97093e9ee609647bd55f434a27bb30a9401": { + "balance": "200951434577471000000000" + }, + "e62812ad5834747f17c92435d863639e84d132fc": { + "balance": "3017271391299000000000" + }, + "e630b92aa8443eb077e1f6990a2e194d99cf53ec": { + "balance": "1000000000000000000000000" + }, + "e656fd1641c15e1a4b753be41bc4aa438b44b42c": { + "balance": "26972744083000000000" + }, + "e663f0257b98dfa80602a2af1bea1f901c4a7612": { + "balance": "97075813547000000000" + }, + "e66e411a8a9d019b53bf2e0a7e44703e1aa93ac1": { + "balance": "25000000000000000000" + }, + "e6712675d13fff27af43bb1cb3f2f283755bacf5": { + "balance": "227572496234000000000" + }, + "e68e8f04b2cff484da2d41dd639ae8880920f781": { + "balance": "20000000000000000000000" + }, + "e6972b5d7e0fe8c722dec9146b92f89291a0207a": { + "balance": "2115924954211000000000" + }, + "e698b491330cb55ecc4cc4b74015cd94eb927fc4": { + "balance": "1038111785278000000000" + }, + "e6c411e67b90109dbb0fa75f0f07ae8a504e9637": { + "balance": "123792105420000000000" + }, + "e6fb1dabc624edb45b040ad66f30dae010a6b634": { + "balance": "16076893670852000000000" + }, + "e71dac161206e7d3686d13b98fd922ab73587988": { + "balance": "500000000000000000000000" + }, + "e773f9be9b3f4b35ac149b4d759b9e47c8000bdb": { + "balance": "329623043336000000000" + }, + "e781cbbd2dccfdf68595d54fa44104a80d52dd22": { + "balance": "188679476509000000000" + }, + "e793666c7850a409b1d5494f576d122e85cfed9c": { + "balance": "1141845197779000000000" + }, + "e7a5527c6deb922e9f84309c502048f49f0c8f14": { + "balance": "81415566708000000000" + }, + "e7b0f75f9c69ae464b1b63cf295555d0815fc532": { + "balance": "10000000000000000000000" + }, + "e7b43cc673e321e607190a6fde996b71508f4d81": { + "balance": "103958781426000000000" + }, + "e7bfcf3125e37755e57804dfe4479657b212a8ca": { + "balance": "10000000000000000000000" + }, + "e7d33cbbd4eb38365c5be04ce32658a5ac741cfa": { + "balance": "1545192252109000000000" + }, + "e84cfbd7844f6aa3e830258a6b1069b6a7ff5b7e": { + "balance": "543989509107945000000000" + }, + "e8aa0cbc5c1f59fadf3ec122fa8a59ebfc60b5b6": { + "balance": "61271973066000000000" + }, + "e8adb5303c30a8ee044dc09c49818c02a16f4254": { + "balance": "737375689166000000000" + }, + "e8aeef5114e19d467c3064938c5965d04830f2ae": { + "balance": "51130466380000000000" + }, + "e8b5a83497198a513fb2e244bcf05f9d4cf09d62": { + "balance": "10000000000000000000000" + }, + "e8b6818cf0d24bd0e7ded854b3d368662a150dab": { + "balance": "63697741112000000000" + }, + "e8b68b9cb24169fd688db7a626d79d0363777c75": { + "balance": "427222669643000000000" + }, + "e8b8b57b23ea953943da3ef7efaefced9cdbb44c": { + "balance": "16000000000000000000" + }, + "e8f85dca364d26c2149b767904c6c06249c3d88a": { + "balance": "199342917246000000000" + }, + "e916c7801cdcf1b6cf640fcd9dcc1e3148c80105": { + "balance": "9000756000000000000" + }, + "e93cbef13277324caae7816c3d601e2f6bb42589": { + "balance": "121000000000000000000" + }, + "e9415fedcdf8939b551999900128530195a2a5f0": { + "balance": "85165078941891000000000" + }, + "e9a79ade714ce48a07fe88532a20d8f8ed27bac9": { + "balance": "30768493367842000000000" + }, + "e9b35c7ca775661bbd3a4844e2c6bc5effcdea58": { + "balance": "134719523000000000" + }, + "e9b819dffb600373bfd1b1608fc9744cc9167855": { + "balance": "1537634693002000000000" + }, + "e9c5ef50d4a194e53928659b4486a1c456df9e56": { + "balance": "50000000000000000000000" + }, + "e9e21f4523b11567516f6fc525e8967ac707f988": { + "balance": "2498740681000000000" + }, + "ea02821d6c730e061a9947b75188eb8bc0bbf9f1": { + "balance": "12822292582000000000" + }, + "ea3bca3a17c7e724ac0e15acab6442f222cd8688": { + "balance": "2789689549000000000" + }, + "ea4f7923d7045a148d50153f5f4620dbd31a74da": { + "balance": "113595858930000000000" + }, + "ea6d4cbae3cfe49ffd36653bb0d64c01b2bbc0b8": { + "balance": "49325017701000000000" + }, + "ea76cd4cff825301932a5c1d3a1de55a0ff00797": { + "balance": "1282028021000000000" + }, + "ea8e4c8c6500856777e2b41832ff00443db291ce": { + "balance": "553674550359000000000" + }, + "eab52191e5afc804b8685fe13d7ad6f5dc64fc12": { + "balance": "244412435341000000000" + }, + "eac1b0868b710e40d6d5c66a461dfc8f78abbaa9": { + "balance": "10000000000000000000000" + }, + "eacac2c75920b8f6e65f37ad81deb113d526d031": { + "balance": "53028042076000000000" + }, + "eacc9ef8b534143560f420031a8a7f030ff1a36e": { + "balance": "381111853842000000000" + }, + "eaf2cc9fdfe6272de269f32486b2d4c248a05afe": { + "balance": "2793234915237000000000" + }, + "eb0220406832a8a5d4f242538e82c80bd83d0ac6": { + "balance": "10000000000000000000000" + }, + "eb20efc0e0af48c8e6da4b21efa9c9f02d92d29f": { + "balance": "152958793764000000000" + }, + "eb41bce8e3aac2bcf662854a3151e3c83d98c6f3": { + "balance": "219455327737000000000" + }, + "eb44c591306972c29a7084079720d8ee5fb9b0a1": { + "balance": "49000000000000000000" + }, + "eb4b26ab55dc35df2e78d47a90fc43148a6de881": { + "balance": "12139574483030000000000" + }, + "eb4f53510db5edcaad6ea169e521bd094e8da4b1": { + "balance": "100000000000000000" + }, + "eb4fbfb7c0082aa0e7edaed934c5166fee955e5b": { + "balance": "299713748180000000000" + }, + "eb6067ab544af6289a73111e7693dc449d5c2134": { + "balance": "20000000000000000000" + }, + "eb86fea82d10d309b1365237e4855a48684e0e49": { + "balance": "81510415589000000000" + }, + "eb8abbcadeb6e19ab4392cded7a407c8d5df2d5c": { + "balance": "25000000000000000000" + }, + "eba44ca2d6f36df8221a2021bf0644cf6cb59452": { + "balance": "500000000000000000000000" + }, + "ebacbc0eace170f66415df48f74d98eb31828d15": { + "balance": "19046465915296000000000" + }, + "ebc72fb8a1029139d8abdc08da23dc559f87e1a8": { + "balance": "24177703742991000000000" + }, + "ebd561bb9001991cb6b02c8ff9e7ece8a3d73dde": { + "balance": "6684606759000000000" + }, + "ebe1dc3ee857ae4add6fa6636b678af8451d1701": { + "balance": "1485349608007000000000" + }, + "ebe68dc904c737be83aa2ee7f613dd51a6d436e4": { + "balance": "11206782120918000000000" + }, + "ebecf4db55a99f018bf136173ae823528f211380": { + "balance": "191817711082000000000" + }, + "ec15ad0aafe0c0f18089de50b2397509e15a20de": { + "balance": "20000000000000000000000" + }, + "ec2e56973a6cbd8b37d0294b16ef806ab5943ec7": { + "balance": "12031630315394000000000" + }, + "ec432a6a4685ebf6c1e872001d1de246140c8d98": { + "balance": "280056522277000000000" + }, + "ec866ba1bdadb91ca25f5ae035b0f69421ed4377": { + "balance": "431849961155000000000" + }, + "ec9be854224d3d371b79ffc1230fe704ba03be2b": { + "balance": "3692428502391000000000" + }, + "ecc2d6e129c7daa37a93f559c6d4f575171d8386": { + "balance": "20000000000000000000000" + }, + "ecc3aca2a21cb317c5b9debdcb2090f3931d5cd7": { + "balance": "100000000000000000000000" + }, + "eccc9a49ff40aa4b07aa0e1271cfb6713de683dd": { + "balance": "617207728367000000000" + }, + "ecccf24530629033fd6234ae32bde2052ebaa640": { + "balance": "16000000000000000000" + }, + "ed16770d5a56dced87224d4ff68a361a2285fef2": { + "balance": "10000000000000000000000" + }, + "ed23b8e782d5ddf203f9b80e5df83ec32e484fc6": { + "balance": "5000000000000000000" + }, + "ed3244e4168e669ae9d54175173c3f0f0e7c4c7a": { + "balance": "803397672115000000000" + }, + "ed48f39d3f022b321c0864d4955e1cdc8cf54834": { + "balance": "64000000000000000000" + }, + "ed4cb42fa6737cbbbf095f181e1425b3bc3ab4f6": { + "balance": "8974148344000000000" + }, + "ed560f7d83c27a26965f84dcface3930bc447fc5": { + "balance": "2092287996000000000" + }, + "ed6ace91369ec3b06cce474e67d1ce4aba6475a6": { + "balance": "1227081000000000000" + }, + "ed8249dd4a91f70176ffff310e5546e7e0c30b91": { + "balance": "813069034369000000000" + }, + "ed8987fa3d4d42bb8f009c99cda5868633d94f5a": { + "balance": "174952234860000000000" + }, + "ed99b72a58a519ca7aa8f46b8d254c3f1eeea0d6": { + "balance": "10000000000000000000000" + }, + "edb720c9bde4801e204e90282de2a6cf1c44c4ad": { + "balance": "10000000000000000000000" + }, + "edbea23cd0cfde3705d83aada88e78b9f4bb1a50": { + "balance": "4000000000000000000000" + }, + "edc1f174655205bb961ddf94a997cdfd24f1c2ed": { + "balance": "65211537189000000000" + }, + "edd1d2dcba881202bc546943194d64e59bf74bfd": { + "balance": "10000000000000000000000" + }, + "eded28fbd959f2351b4252abc71f0e809562fd4c": { + "balance": "1000000000000000000" + }, + "edfe4d4c83c7db76e5e8a9ccafa34d9841669dac": { + "balance": "2578239411258000000000" + }, + "ee1ef79de869b89334d883ba766e65150f3f6cf5": { + "balance": "779780646165000000000" + }, + "ee27b2da240e862f0848d31116a7b4ed91835c8d": { + "balance": "111637484977461000000000" + }, + "ee3195bdb69e97796911c63fdd3fcebad61ffe9b": { + "balance": "214483035823000000000" + }, + "ee43306530c21793c4fd6039b51cf54fbc912bf0": { + "balance": "374531713769000000000" + }, + "ee4515e30ee1b8dba4779ef213d89e8dfff26ea6": { + "balance": "1166743135013000000000" + }, + "ee591e9ca7948b8485eb210e2a3f706b97e6f9e2": { + "balance": "27793157052774000000000" + }, + "ee5ba6c854d633a04f7656d311817e5104c6de14": { + "balance": "289361919166000000000" + }, + "ee909db4ee48bff3adb9e43db940245a8e5e094d": { + "balance": "582143490064000000000" + }, + "ee94d1afa82de70eb65aad0662f48ef3170495cb": { + "balance": "242490158636000000000" + }, + "ee97e18e09bbb16137a7b4aaae464e97d70e6606": { + "balance": "442709862861000000000" + }, + "eebe957af00050c2841f3ef8768c6a77a5394012": { + "balance": "9000000000000000000" + }, + "eec052f4e2902f7cc496162ca6525997d2b3ede4": { + "balance": "69349303517000000000" + }, + "eed30e1a939d5f0b4a39598967a5f149a7b7cb8c": { + "balance": "1637195595000000000" + }, + "eed7bf1ba39bfdad0ce1b6b8d4c9bb31dc1a9843": { + "balance": "203331701702000000000" + }, + "eee276140ea24e36eccb4fd748f675df1acd3b73": { + "balance": "1000000000000000000000000" + }, + "eefb33b290741c4cded862cea777efe4b14a76da": { + "balance": "64000000000000000000" + }, + "ef17a60d15ecf68a62b4bfd5e3acd6201e1931af": { + "balance": "113292502078000000000" + }, + "ef3f4df42127d3e94b4b5883ca97ee63f90b68b5": { + "balance": "17819622000000000000" + }, + "ef4aa6833a69cf72fbf3eaac57da236970aa4241": { + "balance": "1638520372091000000000" + }, + "ef958a1db06e5b8e12547148f3b01da9a8841aad": { + "balance": "12847752197000000000" + }, + "ef9fa861eefe12a3b4c161a47db5d94b1fa873a9": { + "balance": "49000000000000000000" + }, + "efd9b1ce6bc3932961e41e875edaaa367d318b36": { + "balance": "1626378762077000000000" + }, + "efdce7f577c77f0dac6afc78dcbf5ebadc1c3a73": { + "balance": "627500067619000000000" + }, + "eff3f26bc45638d89f28b3ea7a5471af0b680b72": { + "balance": "1650959950189000000000" + }, + "eff6d78814ddae79d6d09d830dd44de55f3f919d": { + "balance": "44409266093000000000" + }, + "eff739e22b9aeb3781dc301da70761fdd178f08f": { + "balance": "574842224234000000000" + }, + "f0059b3c8a32d3d012b4fcb993431a484b67762f": { + "balance": "516933429840000000000" + }, + "f06747d8e2c76b8827bbd0bf4ea3a68d390ee8f3": { + "balance": "8124594790100000000000" + }, + "f0e29fc0aecc36d1bdd818148878ea7d01957476": { + "balance": "79821431871000000000" + }, + "f0e42acf4e027aa61ac2f56e3d2c171ec0fd6ebf": { + "balance": "672499252575000000000" + }, + "f14338307bc5e6ab71fa202447ce240947568b3c": { + "balance": "13990001528784000000000" + }, + "f14f9a1206eb436a3d2e4ba9b3976137f67a6596": { + "balance": "1086707451000000000" + }, + "f15fcf1772fa5b2a578ce4f9270996430d533000": { + "balance": "496026996898000000000" + }, + "f18c691a5827ff1fdc44b54bd9a64fabd53c1cf4": { + "balance": "3112912699000000000000" + }, + "f1960640b52af75fc71101aec2611499c17cd9c6": { + "balance": "195957678178000000000" + }, + "f1abf01ddd474949713bd7fa67ec81d6b56c87b7": { + "balance": "121000000000000000000" + }, + "f1b93a6cfd4b1c7e0e89ebed119c5fe55af2035e": { + "balance": "1000000000000000000000000" + }, + "f1d14c7659a10ff38f4ea74ff5b07ac035984b6a": { + "balance": "9986323720000000000" + }, + "f1dbf37470a2c4fef98b1023026870ae8f7df2c0": { + "balance": "132757602000000000000" + }, + "f220b958b619d5d848597dd00824ab8b1401ebd2": { + "balance": "1461699635849000000000" + }, + "f2484911e0aa707f88d9dd970db21e8f24b9de2f": { + "balance": "20000000000000000000000" + }, + "f264c15790fd7a36d9ce7a454f6bfbe878708a50": { + "balance": "64000000000000000000" + }, + "f2662356cb3ae7b82efd6c82c3591ee40854892b": { + "balance": "50000000000000000000000" + }, + "f27ae5783b96ef637bde4179080a8f5af63ae692": { + "balance": "784985848611000000000" + }, + "f2a62fc212717e411f72f9a694e30b8da21bb31b": { + "balance": "614971541702000000000" + }, + "f2d0a9594231efb87ac833c365b80944251f29d7": { + "balance": "478622654587000000000" + }, + "f2df99a3df0b9b448d0ea48b9fd5cb1ce9ce50cf": { + "balance": "851116673037000000000" + }, + "f2dff0ae1f5f74808624e4f26fa814e4e19c216a": { + "balance": "404457730686000000000" + }, + "f2ea1ac6282364ad5904c6f058827a4382111d94": { + "balance": "5502482915000000000" + }, + "f2fafdcdb2d887eb13b5362eb76be2a682868643": { + "balance": "6174264174000000000" + }, + "f314adfc2fbf632a6e5d8a261385b6054aca31b6": { + "balance": "1267558242119000000000" + }, + "f31a66a88394ed7dd6609aff07dd26a60a219bd8": { + "balance": "346102834465000000000" + }, + "f3535f2b42d8613363e6d9717cc21a8ec3a74fe0": { + "balance": "35723093185103000000000" + }, + "f36a149466982c030ce3b9717f34b593613804d5": { + "balance": "10000000000000000000000" + }, + "f3828b0eaba4acfbbcf3c58277ceb4616a34b630": { + "balance": "633998941064000000000" + }, + "f38f767eeb8002ef051b32fe2f40193bf0751d92": { + "balance": "50000000000000000000000" + }, + "f39bce177817a7338b1adaf713222e515c0d762b": { + "balance": "1128231726329000000000" + }, + "f3ac7ea27a1cefc7787e5ba54dacfd8385ee4afc": { + "balance": "11364602682758000000000" + }, + "f3d9ea511335ed418b1837766da11832aedf5578": { + "balance": "29188596603509000000000" + }, + "f3ef05ccd19df167e06797d962f6afe16037e134": { + "balance": "144000000000000000000" + }, + "f3f630148eccea0ad7bd67bb806bd5676a4ea4cb": { + "balance": "87187208643000000000" + }, + "f3ff31784e0b8c3cd2f7e18cfd07c682a42d1c8d": { + "balance": "10515373125000000000" + }, + "f40b976e8519a2c97f64783bca495ed3f2e4a7c0": { + "balance": "780184503985000000000" + }, + "f416a3af7f3181ad9c8a916989949d35b0b636ec": { + "balance": "16114504005275000000000" + }, + "f419759927eea6afe77701c4cf4a98791a709ad1": { + "balance": "1032589347112000000000" + }, + "f4368f9c9ad8236b56413f174562d6b6fef21d1c": { + "balance": "5447645343000000000" + }, + "f43c57f984b0e2b7ce4d703e82f41195585504a4": { + "balance": "1135809111749000000000" + }, + "f4449f52895de96a4638c927dc389f010bbd530c": { + "balance": "693196063498000000000" + }, + "f449bd417a674c8bfa1db3a3e09c2b03da0f0c04": { + "balance": "106343287319000000000" + }, + "f44dec8340986c06d64dc98d78772a8a9cdc41ec": { + "balance": "1379381904815000000000" + }, + "f4642be1a7685aea0dc7b362d36f58f15d806b72": { + "balance": "4717509847323000000000" + }, + "f4712925f57391043e0cc2e671f33124a0bc8613": { + "balance": "419736833200000000000" + }, + "f47317fba5927dd8dffc4049d4f3277fcef503d6": { + "balance": "149279442682000000000" + }, + "f47ce4c5aaef82692e47f7a810ba38d1faec0eea": { + "balance": "10000000000000000000000" + }, + "f491ffc412bf142788bb82d48bd4eccbe9e0a286": { + "balance": "77276422315000000000" + }, + "f4a1e27e669c29f15b9f89ac15f702340a135743": { + "balance": "324000000000000000000" + }, + "f4b5cbfa50a6c4f5f7db7a93fa565362cc7aceac": { + "balance": "195951823248000000000" + }, + "f4b949c6e10615b651675016f0d7d6ff64e31aee": { + "balance": "35516207325223000000000" + }, + "f4c5e2f043ef3548a2c1c27d968087bec65e2f7d": { + "balance": "100000000000000000000000" + }, + "f4c79ea9c6f7297e016c39296d86f0304070c31d": { + "balance": "71036374423000000000" + }, + "f4dde3733a72872a7efc095cb412672c50928f1b": { + "balance": "129914864759880000000000" + }, + "f4ed736a413464eb93f8a430e093a64f0bd4222d": { + "balance": "10000000000000000000000" + }, + "f4f07e45560fb63d5207ed7e8d7cf4fe29e06d18": { + "balance": "293103814503448000000000" + }, + "f50eac35eef0a1bfa23ba31020ef60e89bf8e9df": { + "balance": "10000000000000000000000" + }, + "f51236dfd888929ccb2fe1f1fc5554abc5df4ce2": { + "balance": "25000000000000000000" + }, + "f521eb42e9092350f2ad4391ddb42bfe7abb4db9": { + "balance": "217462745186000000000" + }, + "f54e7062b6a9a8b283acf00fcbad58aca0737676": { + "balance": "7327357122437000000000" + }, + "f553301efd81629d0856d9c95c70f4a962e602ed": { + "balance": "1500355826530000000000" + }, + "f55c555b0991b2413f2f2764d8ed6a0d77825965": { + "balance": "1174679810163000000000" + }, + "f56ff110d521ceaec29dbf2842f1e78b24463cea": { + "balance": "20000000000000000000000" + }, + "f573fec366236ab87ba041f7dc6a88d92b1fc9b7": { + "balance": "4659857040000000000" + }, + "f59987743b239379aac9353e17e0e4442aa2c684": { + "balance": "25000000000000000000" + }, + "f5a9ca298e88c5492dd44a66d815b649c2f01d39": { + "balance": "95879585325000000000" + }, + "f5b4933164c55b5ba99db906ecaa52bba4f95164": { + "balance": "25663623936000000000" + }, + "f5d20af68c6fed98144718b6beab82fde00dfedc": { + "balance": "16000000000000000000" + }, + "f5e49ce72be9b17ff39688860e5cf6fd500a886c": { + "balance": "106142276914000000000" + }, + "f5f472405a4530075805fbc11928544770fd61fe": { + "balance": "64000000000000000000" + }, + "f62096c7305eb97b221bb637f4269246fe59262b": { + "balance": "855993602798000000000" + }, + "f622bf9b8f7be2f75d5ed73d318a0e7fa62a587f": { + "balance": "20000000000000000000000" + }, + "f6231f31d524ccc444bd046123ba33bc224bdd52": { + "balance": "97550810879000000000" + }, + "f641b4a721dcefa497274fd06888eb998b9bc038": { + "balance": "39401014566340000000000" + }, + "f64f0c5172c99d74b2450a4685c3ec715b379922": { + "balance": "28337413668000000000" + }, + "f65841061cd55cbf20843d9594bce9ee133aa644": { + "balance": "9064540188290000000000" + }, + "f65f0106f3d148d0660547f0683ded4dffc12fe9": { + "balance": "87334071785367000000000" + }, + "f677961296ed933db9e1dd887711387540c0436d": { + "balance": "3982789899000000000" + }, + "f68ba7530f423b8df1625cee36f8df2363a57c49": { + "balance": "5000000000000000000000" + }, + "f69c6eaf077b795f19a9590ee8b578543558e4c4": { + "balance": "10000000000000000000000" + }, + "f69dfe3f0f76e50e2850e44e9e36b6966e277eaa": { + "balance": "288231750575462000000000" + }, + "f6a73c4b958b4d6044f3f4da7147d0fa80e2ea31": { + "balance": "50000000000000000000000" + }, + "f6b0864be5f7bbc4210a3420aa3ead614a8fe7e2": { + "balance": "880968828000000000" + }, + "f6f43a6d9517471436d2ce5047a2b707580e7149": { + "balance": "20000000000000000000000" + }, + "f6fb414d1ca7c29be35b5f97096c817bbf70b070": { + "balance": "15156317416682000000000" + }, + "f707b491ac27b2d2e5e1f9d4123635ee0af92c5c": { + "balance": "500000000000000000000000" + }, + "f71179583a471767a1b399842d7d29caefe57a5e": { + "balance": "429648186876000000000" + }, + "f71ed909eca6bfd574cd670389bc9250493d686d": { + "balance": "38189267531000000000" + }, + "f72ccdc70b7878cdb94f42ee72ca5b4b35a46238": { + "balance": "86065647347000000000" + }, + "f74035e85dbfdb961037bf689ee7dfdcfaf32d64": { + "balance": "398451682882000000000" + }, + "f77668db085a87b0a0405a275e1c2516d3e02b66": { + "balance": "10000000000000000000000" + }, + "f78990d9e50876b49f933e9d74bda44197e9aa7d": { + "balance": "51984216556000000000" + }, + "f79b9df28b7d94d1b4491fca1cbe50bd36aedb3a": { + "balance": "11546152485156000000000" + }, + "f7c773b89be413848dc4a96f064693a0c3a2eab0": { + "balance": "7084247258755000000000" + }, + "f7e29c20bb0023e9ae079da589346fdfd960dae3": { + "balance": "93132014782000000000" + }, + "f8124428ea619d30a335ecc4c2f64e36500abdcb": { + "balance": "8838170798391000000000" + }, + "f843c9d70226e6c2c8cd4cef78e2db66a8eac027": { + "balance": "498377670361000000000" + }, + "f84bb3c0d872dcdbe99d6abcc57c6b5c2b2e35ad": { + "balance": "1405105232436000000000" + }, + "f8679b915ae94e4668f2e27d1094cbb2d97cf428": { + "balance": "1000000000000000000" + }, + "f86dbb82c634cdfa818e4d0dbcfcc9a5c47a9ddb": { + "balance": "196000000000000000000" + }, + "f88bad7726aa66bc1d0ca5824044072f3551fd15": { + "balance": "37432374800000000000" + }, + "f8ab07d0751a2c283ebe2a7e28c5b6e57867e1d1": { + "balance": "25000000000000000000" + }, + "f8afb4f5684c56ff7ce71b4e4cf7e42062470e08": { + "balance": "10000000000000000000000" + }, + "f8c28df0d1a0982289ddfa2a6d562e5c75a5dd01": { + "balance": "1447386977682000000000" + }, + "f8cca137f9c12b48eafd43f038e55e2d3c481919": { + "balance": "35370515421000000000" + }, + "f8e50d1816a5e5c649756ae208209b03b1ece0c3": { + "balance": "48449640035000000000" + }, + "f8fb33ba1d93112d9c3672806e0939083f09a88e": { + "balance": "419743187776000000000" + }, + "f903bebfcc6a7050fc2c5bd14248af9b300f1600": { + "balance": "473363252199000000000" + }, + "f90ab9078f26dd881fb054b4b6e3b3e17fa94718": { + "balance": "156449634345000000000" + }, + "f93e3f392efc057f0af3a91416858a515c1ed996": { + "balance": "1147663044625000000000" + }, + "f94eac538ca66931869c312acb67721c4337842f": { + "balance": "368103335377000000000" + }, + "f94fda503c3f792491fa77b3702fd465f028810d": { + "balance": "317241487661000000000" + }, + "f95dcedbefee8ed01086c91d91a4c115ad8fc947": { + "balance": "147059838786000000000" + }, + "f961a293bbce366a6fcc98d2ba0342e2ef3c5519": { + "balance": "10000000000000000000000" + }, + "f966fdbc4a42f055f8f52d31c23ad7b6a07a5e22": { + "balance": "10000000000000000000000" + }, + "f9a3a61a2f1469835240bb0641eae40c07451e30": { + "balance": "218000000000000" + }, + "f9adcf232180378b08a46d6c8d9d97f01802e01b": { + "balance": "15658216517944000000000" + }, + "f9c68991ff7ac307e41ea1c673f8ebb1a6afbd99": { + "balance": "10000000000000000000000" + }, + "f9cc0c60431d7bdb0c7581a9ae7f011b0abefeb1": { + "balance": "16000000000000000000" + }, + "f9d43c329b61ca2169600e45c8fad3c94226adb8": { + "balance": "120128558137000000000" + }, + "f9ef5d4e2ca8888216b939d3d938438a34dd9da2": { + "balance": "144000000000000000000" + }, + "f9f3d14cd3bd09e2c4c89035b4f50e93f6175cef": { + "balance": "725000000000000000000" + }, + "fa0f5a03601bd1fc76865cdd69d9671ba6073592": { + "balance": "225298289139000000000" + }, + "fa12f10db0eb552b719194becef20af9f45de8db": { + "balance": "1012484659496000000000" + }, + "fa146c58a0709951bc2e9bccddcd002c5a0bb7dd": { + "balance": "199563276701000000000" + }, + "fa159185c156f35fa450b77c48846c2dab6349b7": { + "balance": "100660066567000000000" + }, + "fa193312655f79c7b0ee7d7ef904486836180026": { + "balance": "48141690266000000000" + }, + "fa2484de744918bd8c91350fbabc0dab8b8a44f0": { + "balance": "36000000000000000000" + }, + "fa36dc463b026d8edfeb8ac4acac43a51d643457": { + "balance": "9608761064478000000000" + }, + "fa84199010be2bf53e803c23771e0d15fd025386": { + "balance": "1474902394742000000000" + }, + "fa958bbfa367a745bcd0904db2c4e30445edaefb": { + "balance": "175679888121000000000" + }, + "fa98bcaeb55285ad7ead12ccaa15cf488f567ede": { + "balance": "136105143781000000000" + }, + "faa1be631da42b41a026774f4166c1b831ef41e9": { + "balance": "86358861589000000000" + }, + "faaa857e7f149968434f313ab8db596e1b0ae75d": { + "balance": "36000000000000000000" + }, + "fac2b85ab274055cf1415d57394e8aca4541857d": { + "balance": "289000000000000000000" + }, + "fb23a508ccdb4e91b252f5c06c465c55ed59b1db": { + "balance": "14698710175236000000000" + }, + "fb24d4e47ba70aa4b984372b4852ad3d082daa24": { + "balance": "4526648424830000000000" + }, + "fb27a7e8b8b4ae43c69ce025b46187e538608769": { + "balance": "121000000000000000000" + }, + "fb2cdb5e85872f52c99985f219b8fb4125c6a8b7": { + "balance": "8568367153000000000" + }, + "fb3d76c8165bcb3c93fd3b2b10c20588d0fa97aa": { + "balance": "500000000000000000000000" + }, + "fb5161b2cc9d48a53f47d66002905f0458e3cd9e": { + "balance": "225000000000000000000" + }, + "fb72756c4845f18ab35d29f632b662c0c0d4b94f": { + "balance": "883095068524000000000" + }, + "fb8b7efb02ea5292304c0f0abc8c555684653587": { + "balance": "10000000000000000000000" + }, + "fb9ee61e337a5c7b57c5140e84919101570e2cb7": { + "balance": "16000000000000000000" + }, + "fbae69f44b116c186a86cb0de79323ca3d6b99eb": { + "balance": "1359504686067000000000" + }, + "fbbd399eb9e5d3dd67efc48927973601dcd84321": { + "balance": "2049018637367000000000" + }, + "fbc9a3c3c429990cc306710b3dd44174dcc72ad4": { + "balance": "55507457947000000000" + }, + "fbce66a6898ecd70893db6b4b8c3d00afef8e20b": { + "balance": "20857164902458000000000" + }, + "fbe8fe04084fc93dff8228861fe100bfeeb057b6": { + "balance": "10000000000000000000000" + }, + "fbfb717f902ad79ef63565f9ab57f041ff5f7626": { + "balance": "16000000000000000000" + }, + "fc0b6c8c6be79bf0c9554f7855dc8c4a617d02c9": { + "balance": "17347593956000000000" + }, + "fc17518d05e605807847bbf6f407da89037bca00": { + "balance": "1796383702108000000000" + }, + "fc2793424c809cc80938a1be1292813adbc8ac8c": { + "balance": "10000000000000000000" + }, + "fc35930abb108ae6cae33fd065dfb799808ea326": { + "balance": "912737460000000000000" + }, + "fc5a9209799e563ae8d958774dc86345a3bc7ed2": { + "balance": "29049176573000000000" + }, + "fc8011850c09c9288e737ea58ca5c15cded6dc8d": { + "balance": "10000000000000000000000" + }, + "fc9183ed137be071ad183d025395a0ebe2674654": { + "balance": "500000000000000000000000" + }, + "fc98c9d88b1fbbb68dbdd6448aa6a32e8282800d": { + "balance": "900000000000000000000" + }, + "fc9f4b9da7a46c2bfcd50cafe1f892b9984be0ee": { + "balance": "21577116424370000000000" + }, + "fca525b732a673b953f1c23083c276cc8cbcb86c": { + "balance": "77653618624000000000" + }, + "fca57b6a4798f33478b6e23622173cda3fe1b9a0": { + "balance": "793368066098000000000" + }, + "fca79b446c513a7bed643603c42f35ff0fa89f49": { + "balance": "998082799053000000000" + }, + "fcab42f7f07735a7b09074c1f1769287069c88c8": { + "balance": "94824830574000000000" + }, + "fcacbbc6810c586522012ad32c3dfac80eb563b4": { + "balance": "10000000000000000000000" + }, + "fcb38809b63810b6673dcb4c947e01f7b49fb1b3": { + "balance": "725937240372000000000" + }, + "fcc0e531d9f6265672aa885af361534464a11015": { + "balance": "22121462657000000000" + }, + "fcc49c62d7738fa1b92aa6a69a12b671e4c7c8d9": { + "balance": "50000000000000000000000" + }, + "fcc95394fd796ca5bd8f3814883b1150d74dd9a5": { + "balance": "144000000000000000000" + }, + "fccdb068dfd599d7d5c290a6ae65eba9151d5b29": { + "balance": "5369426564000000000" + }, + "fcde41ae28bdf9084a28f47a9348d8aac5b3dd43": { + "balance": "409599263197000000000" + }, + "fce5816f066ca32d1fa02e9e8b5eb8a7fa3e4dea": { + "balance": "1193272309645000000000" + }, + "fcf9fb8996d6d9175ade6d6063be0742de20ea1f": { + "balance": "16852526239339000000000" + }, + "fd10488d55e6861cb67f7f50950d78892e7032ad": { + "balance": "165069902909000000000" + }, + "fd23e8263d89256add0dfe93da153d305ad917c7": { + "balance": "26633825496000000000" + }, + "fd3a98cc3b3f1439af35f806de2fb05fef98f279": { + "balance": "1043321187464000000000" + }, + "fd3d79185a91984a117ee6f9fd304725875094e2": { + "balance": "2349991833898000000000" + }, + "fd5e6ac22634f04ec4ace5da8996c2b7b70b22f4": { + "balance": "10000000000000000" + }, + "fd62ed1cf7a535c989fbd742b1660205a2f69dd0": { + "balance": "49000000000000000000" + }, + "fd645043bd4d7b71e63e30409b91e9fdda3a86c0": { + "balance": "362957768837969000000000" + }, + "fd7014fc1c70af482115247ff94ff6bdbd3d364d": { + "balance": "743383172317000000000" + }, + "fda0cfe95df9021497752b04863c3ec44d13e853": { + "balance": "15586809617955000000000" + }, + "fdb5b964808bcb974d3e888cbb45bcd57e57c907": { + "balance": "5549247772273000000000" + }, + "fdbaaa865ec38da13e80554b6d0abc437f60d8a5": { + "balance": "3736861227131000000000" + }, + "fdbb8693b3c20c0eac5fb585e2347d41debbffce": { + "balance": "100000000000000000" + }, + "fdbdaec57829f25ad48e18d94e0b8533f2801818": { + "balance": "6934630922926000000000" + }, + "fdc318ba5b1f8ad33e00528828b93a840592e2fb": { + "balance": "10000000000000000000000" + }, + "fdcf6a997bb10806e4d87eb4222e9f93b4202179": { + "balance": "1000000000000000000" + }, + "fde5a9911a10770d733db4d32ca9a5493478399c": { + "balance": "20000000000000000000000" + }, + "fe39185a6b84378820ee215f630533e658731ca9": { + "balance": "17022202932000000000" + }, + "fe3b1032e524674cba5f329f940c837850fa53ed": { + "balance": "50000000000000000000000" + }, + "fe3bc4ff2c3b66bc582558314b80030407e7de96": { + "balance": "1669870860988000000000" + }, + "fe668dbb1f3de744d16e13e0ed6f5708c2c15d1f": { + "balance": "39974355655263000000000" + }, + "fe95bfb97fa60341f8af2ad621e606b85e3c2e57": { + "balance": "528601649597478000000000" + }, + "fe99cf2a1fbbe7c46e4235b2d135a3a093fcf16c": { + "balance": "7271022106877000000000" + }, + "fec1f6ed4b3ff01e7ebe13fb53f60ee5a3b9e191": { + "balance": "1316316072034000000000" + }, + "fed9bec1b2145452ed5535e4ba29fafac6c35fbb": { + "balance": "10799354586000000000" + }, + "fedced7aa1cf3f3a7eec321cc0274759b154ea8e": { + "balance": "11740927210323000000000" + }, + "fef5063701a93ad02676fe0b99d0f4d2da0ccd67": { + "balance": "10178531012000000000" + }, + "fefd5627a408ca099587892ee2a46fa8cc89be19": { + "balance": "458504035686000000000" + }, + "ff1fc0f6f26188cbe18cf65d8a344d3775aecc6d": { + "balance": "81000000000000000000" + }, + "ff4fe483b3c04ebc8d6705c699ecee3e92071715": { + "balance": "1000000000000000000" + }, + "ff51bfe823394b2bce05947a6068bd5158d4af0e": { + "balance": "692533626783000000000" + }, + "ff6652e4e45f6b0f95ad4c9ec2bc80476e3f7fc6": { + "balance": "46457898024000000000" + }, + "ff68246ac7640091e5e58345736b249e036364fc": { + "balance": "2626125272000000000" + }, + "ff6d4b8a8393a503047ff829dbf2bf8e9172dc6d": { + "balance": "2865001878255000000000" + }, + "ff6fe19e056a7211b7e484c2c540d5aa5f1d83e5": { + "balance": "36000000000000000000" + }, + "ff7fa33529e1781c1b2951e57581780b229e3fda": { + "balance": "10000000000000000000" + }, + "ff82d1052538539d07cf3955476cc9a5027d8e4e": { + "balance": "83572023121000000000" + }, + "ff8acfe75afcc1efb1bc44be9f9bb242a94f73f7": { + "balance": "7556034521000000000" + }, + "ffa2b5f1685de9fcf1af4653cd3a584db1beed64": { + "balance": "114892199805000000000" + }, + "ffb1e9be68ae8be8d7d066c473589921e68825a2": { + "balance": "484660652980000000000" + }, + "ffbf91a9d1a6377b7435e3e734132e7b34188dac": { + "balance": "20000000000000000000000" + }, + "ffbff1fab9f2bc2f387d0cc9cc28f6aac533c813": { + "balance": "10000000000000000000000" + }, + "ffc4ff6433ea35544e7a07fda170e62c451301df": { + "balance": "29238210920000000000" + }, + "ffc7534b64a8fe8760e931a710883119d28ae106": { + "balance": "500000000000000000000000" + }, + "ffda6b8e3de72d7f7c18b892e6a8b80b886d5fa5": { + "balance": "214366938289000000000" + }, + "ffddb1fb7521c9772ea4886aaf022c4375ef904d": { + "balance": "554864446437000000000" + } + } +} diff --git a/ethcore/res/ethereum/tobalaba.json b/ethcore/res/ethereum/tobalaba.json new file mode 100644 index 0000000000000000000000000000000000000000..e9345c69615de5368c58cd91e0328154e10e2a4a --- /dev/null +++ b/ethcore/res/ethereum/tobalaba.json @@ -0,0 +1,54 @@ +{ + "name": "Tobalaba", + "engine": { + "authorityRound": { + "params": { + "stepDuration": "3", + "validators": { + "contract": "0x1000000000000000000000000000000000000005" + }, + "maximumUncleCount": 999999 + } + } + }, + "params": { + "maximumExtraDataSize": "0x20", + "gasLimitBoundDivisor": "0x400", + "minGasLimit": "0x1388", + "networkID": "0x62121", + "wasmActivationTransition": 4000000 + }, + "genesis": { + "seal": { + "authorityRound": { + "step": "0x0", + "signature": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + } + }, + "difficulty": "0x20000", + "gasLimit": "0x800000" + }, + "accounts": { + "0x1000000000000000000000000000000000000005": { + "balance": "1", + "constructor": "6060604052341561000f57600080fd5b5b60048054600160a060020a03199081167310000000000000000000000000000000000000061790915560028054909116731000000000000000000000000000000000000007179055600080546001810161006a8382610115565b916000526020600020900160005b8154600160a060020a036101009290920a9182021916734ba15b56452521c0826a35a6f2022e1210fc519b90910217905550600180548082016100bb8382610115565b916000526020600020900160005b8154600160a060020a036101009290920a9182021916734ba15b56452521c0826a35a6f2022e1210fc519b9182021790915560038054600160a060020a0319169091179055505b610160565b8154818355818115116101395760008381526020902061013991810190830161013f565b5b505050565b61015d91905b808211156101595760008155600101610145565b5090565b90565b610a208061016f6000396000f300606060405236156100ee5763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166303aca792811461013d578063063a54c91461016f57806311ae9ed21461019e578063170f9291146102055780631cd3e85814610237578063480b4619146102585780636c1247e5146102bf5780637a1a9e60146102ee578063830f0bc6146103205780638da5cb5b146103525780639b2ae4c614610381578063b6f783f2146103a6578063b7ab4db5146103d5578063c55642be1461043c578063e2de215e1461045d578063fe5d935c1461048c578063ff7a071b146104f3575b34156100f957600080fd5b5b600454600160a060020a0316600036604051808383808284378201915050925050506000604051808303818561646e5a03f4915050151561013a57600080fd5b5b005b341561014857600080fd5b610153600435610518565b604051600160a060020a03909116815260200160405180910390f35b341561017a57600080fd5b61015361054a565b604051600160a060020a03909116815260200160405180910390f35b34156101a957600080fd5b6101b161055a565b60405160208082528190810183818151815260200191508051906020019060200280838360005b838110156101f15780820151818401525b6020016101d8565b505050509050019250505060405180910390f35b341561021057600080fd5b6101536004356105c3565b604051600160a060020a03909116815260200160405180910390f35b341561024257600080fd5b61013a600160a060020a03600435166105f5565b005b341561026357600080fd5b6101b16106d1565b60405160208082528190810183818151815260200191508051906020019060200280838360005b838110156101f15780820151818401525b6020016101d8565b505050509050019250505060405180910390f35b34156102ca57600080fd5b61015361073a565b604051600160a060020a03909116815260200160405180910390f35b34156102f957600080fd5b610153600435610749565b604051600160a060020a03909116815260200160405180910390f35b341561032b57600080fd5b61015360043561077b565b604051600160a060020a03909116815260200160405180910390f35b341561035d57600080fd5b6101536107ad565b604051600160a060020a03909116815260200160405180910390f35b341561038c57600080fd5b6103946107bc565b60405190815260200160405180910390f35b34156103b157600080fd5b6101536107c3565b604051600160a060020a03909116815260200160405180910390f35b34156103e057600080fd5b6101b16107d3565b60405160208082528190810183818151815260200191508051906020019060200280838360005b838110156101f15780820151818401525b6020016101d8565b505050509050019250505060405180910390f35b341561044757600080fd5b61013a600160a060020a036004351661083c565b005b341561046857600080fd5b610153610918565b604051600160a060020a03909116815260200160405180910390f35b341561049757600080fd5b6101b1610927565b60405160208082528190810183818151815260200191508051906020019060200280838360005b838110156101f15780820151818401525b6020016101d8565b505050509050019250505060405180910390f35b34156104fe57600080fd5b610394610990565b60405190815260200160405180910390f35b600180548290811061052657fe5b906000526020600020900160005b915054906101000a9004600160a060020a031681565b600254600160a060020a03165b90565b610562610997565b60018054806020026020016040519081016040528092919081815260200182805480156105b857602002820191906000526020600020905b8154600160a060020a0316815260019091019060200180831161059a575b505050505090505b90565b600080548290811061052657fe5b906000526020600020900160005b915054906101000a9004600160a060020a031681565b60035433600160a060020a0390811691161461061057600080fd5b600254600160a060020a038281169116141561062b57600080fd5b600680546001810161063d83826109a9565b916000526020600020900160005b600280548354600160a060020a036101009490940a848102199091169184160217909255815473ffffffffffffffffffffffffffffffffffffffff1916908416179055507f78603ac34f42fe53d8aad96b7b37aeee79dc7ed07c26f57a13cdf64ac72b0f1181604051600160a060020a03909116815260200160405180910390a15b5b50565b6106d9610997565b60058054806020026020016040519081016040528092919081815260200182805480156105b857602002820191906000526020600020905b8154600160a060020a0316815260019091019060200180831161059a575b505050505090505b90565b600254600160a060020a031681565b600680548290811061052657fe5b906000526020600020900160005b915054906101000a9004600160a060020a031681565b600580548290811061052657fe5b906000526020600020900160005b915054906101000a9004600160a060020a031681565b600354600160a060020a031681565b6001545b90565b600454600160a060020a03165b90565b6107db610997565b60008054806020026020016040519081016040528092919081815260200182805480156105b857602002820191906000526020600020905b8154600160a060020a0316815260019091019060200180831161059a575b505050505090505b90565b60035433600160a060020a0390811691161461085757600080fd5b600454600160a060020a038281169116141561087257600080fd5b600580546001810161088483826109a9565b916000526020600020900160005b600480548354600160a060020a036101009490940a848102199091169184160217909255815473ffffffffffffffffffffffffffffffffffffffff1916908416179055507fadcbcb6339ee0b34abbe8d1524c53b813794e1537a43136c6a7768019599625781604051600160a060020a03909116815260200160405180910390a15b5b50565b600454600160a060020a031681565b61092f610997565b60068054806020026020016040519081016040528092919081815260200182805480156105b857602002820191906000526020600020905b8154600160a060020a0316815260019091019060200180831161059a575b505050505090505b90565b6000545b90565b60206040519081016040526000815290565b8154818355818115116109cd576000838152602090206109cd9181019083016109d3565b5b505050565b61055791905b808211156109ed57600081556001016109d9565b5090565b905600a165627a7a72305820e9d3839061bfeb56c1cc57b2b2ec8dd7882afd848b4ae489c218d6f9674316660029" + }, + "0x1000000000000000000000000000000000000006": { + "balance": "1", + "constructor": "6060604052341561000f57600080fd5b5b60028054600160a060020a0319167310000000000000000000000000000000000000071790555b5b610abb806100476000396000f300606060405236156100885763ffffffff60e060020a60003504166303aca792811461008d578063170f9291146100bf57806340a141ff146100f15780634d238c8e146101125780634d655aff1461013357806375286211146101625780638da5cb5b14610177578063a6f9dae1146101a6578063c476dd40146101c7578063d69f13bb1461022e575b600080fd5b341561009857600080fd5b6100a3600435610252565b604051600160a060020a03909116815260200160405180910390f35b34156100ca57600080fd5b6100a3600435610284565b604051600160a060020a03909116815260200160405180910390f35b34156100fc57600080fd5b610110600160a060020a03600435166102b6565b005b341561011d57600080fd5b610110600160a060020a036004351661064d565b005b341561013e57600080fd5b6100a36107a1565b604051600160a060020a03909116815260200160405180910390f35b341561016d57600080fd5b6101106107b0565b005b341561018257600080fd5b6100a36107ed565b604051600160a060020a03909116815260200160405180910390f35b34156101b157600080fd5b610110600160a060020a03600435166107fc565b005b34156101d257600080fd5b61011060048035600160a060020a03169060248035919060649060443590810190830135806020601f8201819004810201604051908101604052818152929190602084018383808284375094965061084495505050505050565b005b341561023957600080fd5b610110600160a060020a0360043516602435610926565b005b600180548290811061026057fe5b906000526020600020900160005b915054906101000a9004600160a060020a031681565b600080548290811061026057fe5b906000526020600020900160005b915054906101000a9004600160a060020a031681565b60035460009033600160a060020a039081169116146102d457600080fd5b600254600160a060020a031663b31610db8360006040516020015260405160e060020a63ffffffff8416028152600160a060020a039091166004820152602401602060405180830381600087803b151561032d57600080fd5b6102c65a03f1151561033e57600080fd5b50505060405180511515905061035357600080fd5b60008054600019810190811061036557fe5b906000526020600020900160005b9054600254600160a060020a036101009390930a9091048216925082916001911663b31610db8560006040516020015260405160e060020a63ffffffff8416028152600160a060020a039091166004820152602401602060405180830381600087803b15156103e157600080fd5b6102c65a03f115156103f257600080fd5b50505060405180518254909150811061040757fe5b906000526020600020900160005b6101000a815481600160a060020a030219169083600160a060020a0316021790555060018080805490500381548110151561044c57fe5b906000526020600020900160005b81546101009190910a600160a060020a0302191690556001805460001901906104839082610991565b50600254600160a060020a0316635caf5a6a828263b31610db8660006040516020015260405160e060020a63ffffffff8416028152600160a060020a039091166004820152602401602060405180830381600087803b15156104e457600080fd5b6102c65a03f115156104f557600080fd5b5050506040518051905060405160e060020a63ffffffff8516028152600160a060020a0390921660048301526024820152604401600060405180830381600087803b151561054257600080fd5b6102c65a03f1151561055357600080fd5b5050600254600160a060020a03169050635caf5a6a83600060405160e060020a63ffffffff8516028152600160a060020a0390921660048301526024820152604401600060405180830381600087803b15156105ae57600080fd5b6102c65a03f115156105bf57600080fd5b5050506000194301407f55252fa6eee4741b4e24a74a70e9c11fd2c2281df8d6ea13126ff845f7825c89600160405160208082528254908201819052819060408201908490801561063957602002820191906000526020600020905b8154600160a060020a0316815260019091019060200180831161061b575b50509250505060405180910390a25b5b5050565b60035433600160a060020a0390811691161461066857600080fd5b600180548082016106798382610991565b916000526020600020900160005b81546101009190910a600160a060020a0381810219909216858316919091021790915560025460015491169150635caf5a6a9083906000190160405160e060020a63ffffffff8516028152600160a060020a0390921660048301526024820152604401600060405180830381600087803b151561070357600080fd5b6102c65a03f1151561071457600080fd5b5050506000194301407f55252fa6eee4741b4e24a74a70e9c11fd2c2281df8d6ea13126ff845f7825c89600160405160208082528254908201819052819060408201908490801561078e57602002820191906000526020600020905b8154600160a060020a03168152600190910190602001808311610770575b50509250505060405180910390a25b5b50565b600254600160a060020a031681565b73fffffffffffffffffffffffffffffffffffffffe600160a060020a033316146107d957600080fd5b6001805461079d916000916109e5565b505b565b600354600160a060020a031681565b60035433600160a060020a0390811691161461081757600080fd5b6003805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0383161790555b5b50565b73fffffffffffffffffffffffffffffffffffffffe600160a060020a0333161461086d57600080fd5b7f8498b6f07a5f800443a5fd85ac8171cf40cda44faea60caabe9b297d9dfa8424838383604051600160a060020a03841681526020810183905260606040820181815290820183818151815260200191508051906020019080838360005b838110156108e45780820151818401525b6020016108cb565b50505050905090810190601f1680156109115780820380516001836020036101000a031916815260200191505b5094505050505060405180910390a15b505050565b73fffffffffffffffffffffffffffffffffffffffe600160a060020a0333161461094f57600080fd5b81600160a060020a03167f31bc112157435aab6dd7e9a059abea7f0fce172e3e68e272a6375bbb01eb96c18260405190815260200160405180910390a25b5050565b81548183558181151161092157600083815260209020610921918101908301610a36565b5b505050565b81548183558181151161092157600083815260209020610921918101908301610a36565b5b505050565b828054828255906000526020600020908101928215610a255760005260206000209182015b82811115610a25578254825591600101919060010190610a0a565b5b50610a32929150610a57565b5090565b610a5491905b80821115610a325760008155600101610a3c565b5090565b90565b610a5491905b80821115610a3257805473ffffffffffffffffffffffffffffffffffffffff19168155600101610a5d565b5090565b905600a165627a7a72305820cae3ca62c5821766e8122461884affeaabdc6c2fe79f5a3200207a536e5b0e260029" + }, + "0x1000000000000000000000000000000000000007": { + "balance": "1", + "constructor": "6060604052341561000f57600080fd5b5b60018054600160a060020a0319167310000000000000000000000000000000000000051790555b5b6101bb806100476000396000f300606060405263ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166318def8ef811461005e5780635caf5a6a1461008f5780638da5cb5b146100b3578063b31610db146100e2575b600080fd5b341561006957600080fd5b61007d600160a060020a0360043516610113565b60405190815260200160405180910390f35b341561009a57600080fd5b6100b1600160a060020a0360043516602435610125565b005b34156100be57600080fd5b6100c6610161565b604051600160a060020a03909116815260200160405180910390f35b34156100ed57600080fd5b61007d600160a060020a0360043516610170565b60405190815260200160405180910390f35b60006020819052908152604090205481565b60015433600160a060020a0390811691161461014057600080fd5b600160a060020a03821660009081526020819052604090208190555b5b5050565b600154600160a060020a031681565b600160a060020a0381166000908152602081905260409020545b9190505600a165627a7a7230582085b261e548fa6e3065a32e485c6417d200c7145f3548c0097d4c92022ac7fb1e0029" + }, + "0x4ba15b56452521c0826a35a6f2022e1210fc519b": { + "balance": "0x7E37BE2022B2B09472D89C0000" + } + }, + "nodes": [ + "enode://147573f46fe9f5cc38fbe070089a31390baec5dd2827c8f2ef168833e4d0254fbee3969a02c5b9910ea5d5b23d86a6ed5eabcda17cc12007b7d9178b6c697aa5@37.120.168.56:30303", + "enode://a370d5fd55959f20af6d1565b151a760c1372f5a2aaf674d4892cd4fd2de0d1f672781cd40e0d4e4b51c5823527ddec73b31cc14ac685449d9f0866996a16b9f@13.76.165.180:30303", + "enode://da019fa5fb1fda105100d68a986938ec15ac5c6ff69d6e4ad3e350e377057f3e67e33aea5feb22d5cdcfc22041d141c8453c77baa64a216fff98f191ca76b3ec@18.220.108.238:30303", + "enode://49498fb8cdcd79c813ccdaa9496a3a4be0a187a3183e99adbc04d9c90b9a62ad59f0b6832f6e43b48e63fbebf74ec5438eb0d6d9098330edf36413d276fedf81@13.80.148.117:30303" + ] +} diff --git a/ethcore/res/ethereum/transition_test.json b/ethcore/res/ethereum/transition_test.json index 7c18fce4e544d08576df823f0153e45169d60ccd..9dc00fd5d8c2437d92ca3936812009fef6be5d73 100644 --- a/ethcore/res/ethereum/transition_test.json +++ b/ethcore/res/ethereum/transition_test.json @@ -8,10 +8,6 @@ "durationLimit": "0x0d", "blockReward": "0x4563918244F40000", "homesteadTransition": "0", - "eip150Transition": "0", - "eip160Transition": "0", - "eip161abcTransition": "0", - "eip161dTransition": "0", "eip649Reward": "0x29A2241AF62C0000", "eip100bTransition": "5", "eip649Transition": "5" @@ -27,6 +23,10 @@ "networkID" : "0x1", "maxCodeSize": 24576, "maxCodeSizeTransition": "0", + "eip150Transition": "0", + "eip160Transition": "0", + "eip161abcTransition": "0", + "eip161dTransition": "0", "eip98Transition": "5", "eip140Transition": "5", "eip211Transition": "5", diff --git a/ethcore/res/instant_seal.json b/ethcore/res/instant_seal.json index e01c927ffdc39e9dd8d88ef475aac1496df8e4ba..36832e698e635df8d42e9dba69ffca196a6b675e 100644 --- a/ethcore/res/instant_seal.json +++ b/ethcore/res/instant_seal.json @@ -10,10 +10,20 @@ "minGasLimit": "0x1388", "networkID" : "0x11", "registrar" : "0x0000000000000000000000000000000000001337", + "eip150Transition": "0x0", + "eip160Transition": "0x0", + "eip161abcTransition": "0x0", + "eip161dTransition": "0x0", + "eip155Transition": "0x0", + "eip98Transition": "0x7fffffffffffff", + "eip86Transition": "0x7fffffffffffff", + "maxCodeSize": 24576, + "maxCodeSizeTransition": "0x0", "eip140Transition": "0x0", "eip211Transition": "0x0", "eip214Transition": "0x0", - "eip658Transition": "0x0" + "eip658Transition": "0x0", + "wasmActivationTransition": "0x0" }, "genesis": { "seal": { @@ -24,7 +34,7 @@ "timestamp": "0x00", "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "extraData": "0x", - "gasLimit": "0x5B8D80" + "gasLimit": "0x7A1200" }, "accounts": { "0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, diff --git a/ethcore/res/tx_permission_tests/contract_ver_2_genesis.json b/ethcore/res/tx_permission_tests/contract_ver_2_genesis.json new file mode 100644 index 0000000000000000000000000000000000000000..b165625a1651e1ff8744b770da69cbbb3b628f66 --- /dev/null +++ b/ethcore/res/tx_permission_tests/contract_ver_2_genesis.json @@ -0,0 +1,43 @@ +{ + "name": "TestNodeFilterContract", + "engine": { + "authorityRound": { + "params": { + "stepDuration": 1, + "startStep": 2, + "validators": { + "contract": "0x0000000000000000000000000000000000000000" + } + } + } + }, + "params": { + "accountStartNonce": "0x0", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "networkID" : "0x69", + "gasLimitBoundDivisor": "0x0400", + "transactionPermissionContract": "0x0000000000000000000000000000000000000005" + }, + "genesis": { + "seal": { + "generic": "0xc180" + }, + "difficulty": "0x20000", + "author": "0x0000000000000000000000000000000000000000", + "timestamp": "0x00", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x", + "gasLimit": "0x222222" + }, + "accounts": { + "0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, + "0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, + "0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, + "0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, + "0000000000000000000000000000000000000005": { + "balance": "1", + "constructor": "608060405234801561001057600080fd5b506104eb806100206000396000f300608060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063469ab1e31461006757806375d0c0dc1461009a578063a0a8e4601461012a578063d4b03ee014610155575b600080fd5b34801561007357600080fd5b5061007c6101ed565b60405180826000191660001916815260200191505060405180910390f35b3480156100a657600080fd5b506100af61025e565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100ef5780820151818401526020810190506100d4565b50505050905090810190601f16801561011c5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561013657600080fd5b5061013f61029b565b6040518082815260200191505060405180910390f35b34801561016157600080fd5b506101c0600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506102a4565b604051808363ffffffff1663ffffffff168152602001821515151581526020019250505060405180910390f35b60006101f761025e565b6040518082805190602001908083835b60208310151561022c5780518252602082019150602081019050602083039250610207565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040518091039020905090565b60606040805190810160405280601681526020017f54585f5045524d495353494f4e5f434f4e545241435400000000000000000000815250905090565b60006002905090565b600080737e5f4552091a69125d5dfcb7b8c2659029395bdf8573ffffffffffffffffffffffffffffffffffffffff1614156102e95763ffffffff6001915091506104b7565b732b5ad5c4795c026514f8317c7a215e218dccd6cf8573ffffffffffffffffffffffffffffffffffffffff16141561032b5760026001176001915091506104b7565b736813eb9362372eef6200f3b1dbc3f819671cba698573ffffffffffffffffffffffffffffffffffffffff16141561036957600180915091506104b7565b73e1ab8145f7e55dc933d51a18c793f901a3a0b2768573ffffffffffffffffffffffffffffffffffffffff161480156103a25750600083145b156103b75763ffffffff6000915091506104b7565b73e57bfe9f44b819898f47bf37e5af72a0783e11418573ffffffffffffffffffffffffffffffffffffffff16148015610419575073d41c057fd1c78805aac12b0a94a405c0461a6fbb8473ffffffffffffffffffffffffffffffffffffffff16145b1561042b5760016000915091506104b7565b73d41c057fd1c78805aac12b0a94a405c0461a6fbb8573ffffffffffffffffffffffffffffffffffffffff1614801561048d575073e57bfe9f44b819898f47bf37e5af72a0783e11418473ffffffffffffffffffffffffffffffffffffffff16145b80156104995750600083145b156104ae5763ffffffff6000915091506104b7565b60006001915091505b9350939150505600a165627a7a723058204982adea2aa10a7b8328ec3829472ee17c62a86957ef6737f2eb729b2c3faf910029" + } + } +} diff --git a/ethcore/res/tx_permission_tests/deprecated_contract_genesis.json b/ethcore/res/tx_permission_tests/deprecated_contract_genesis.json new file mode 100644 index 0000000000000000000000000000000000000000..92fde908089b51eb55fb58bed2f8f3bef0bcbf1a --- /dev/null +++ b/ethcore/res/tx_permission_tests/deprecated_contract_genesis.json @@ -0,0 +1,43 @@ +{ + "name": "TestNodeFilterContract", + "engine": { + "authorityRound": { + "params": { + "stepDuration": 1, + "startStep": 2, + "validators": { + "contract": "0x0000000000000000000000000000000000000000" + } + } + } + }, + "params": { + "accountStartNonce": "0x0", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "networkID" : "0x69", + "gasLimitBoundDivisor": "0x0400", + "transactionPermissionContract": "0x0000000000000000000000000000000000000005" + }, + "genesis": { + "seal": { + "generic": "0xc180" + }, + "difficulty": "0x20000", + "author": "0x0000000000000000000000000000000000000000", + "timestamp": "0x00", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x", + "gasLimit": "0x222222" + }, + "accounts": { + "0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, + "0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, + "0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, + "0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, + "0000000000000000000000000000000000000005": { + "balance": "1", + "constructor": "6060604052341561000f57600080fd5b5b6101868061001f6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063e17512211461003e575b600080fd5b341561004957600080fd5b610075600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610097565b604051808263ffffffff1663ffffffff16815260200191505060405180910390f35b6000737e5f4552091a69125d5dfcb7b8c2659029395bdf8273ffffffffffffffffffffffffffffffffffffffff1614156100d75763ffffffff9050610155565b732b5ad5c4795c026514f8317c7a215e218dccd6cf8273ffffffffffffffffffffffffffffffffffffffff1614156101155760026001179050610155565b736813eb9362372eef6200f3b1dbc3f819671cba698273ffffffffffffffffffffffffffffffffffffffff1614156101505760019050610155565b600090505b9190505600a165627a7a72305820f1f21cb978925a8a92c6e30c8c81adf598adff6d1ef941cf5ed6c0ec7ad1ae3d0029" + } + } +} diff --git a/ethcore/service/Cargo.toml b/ethcore/service/Cargo.toml index 634769d0b5441c7dc307013263e1a33ec56e68f9..634ee55dba46299b73938305e6fed904dd71b7bd 100644 --- a/ethcore/service/Cargo.toml +++ b/ethcore/service/Cargo.toml @@ -5,12 +5,17 @@ authors = ["Parity Technologies "] [dependencies] ansi_term = "0.10" +error-chain = { version = "0.11", default-features = false } ethcore = { path = ".." } ethcore-io = { path = "../../util/io" } +ethcore-private-tx = { path = "../private-tx" } +ethcore-sync = { path = "../sync" } kvdb = { path = "../../util/kvdb" } -kvdb-rocksdb = { path = "../../util/kvdb-rocksdb" } log = "0.3" stop-guard = { path = "../../util/stop-guard" } +trace-time = { path = "../../util/trace-time" } [dev-dependencies] +ethcore = { path = "..", features = ["test-helpers"] } tempdir = "0.3" +kvdb-rocksdb = { path = "../../util/kvdb-rocksdb" } diff --git a/ethstore/tests/cli.rs b/ethcore/service/src/error.rs similarity index 69% rename from ethstore/tests/cli.rs rename to ethcore/service/src/error.rs index f90ec463dc42a4d721c3bbeac9067e7d2207d5ab..bb403d0bfc06d0184756e8f5268b4ca5086f795e 100644 --- a/ethstore/tests/cli.rs +++ b/ethcore/service/src/error.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -14,3 +14,17 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +use ethcore; +use io; +use ethcore_private_tx; + +error_chain! { + links { + PrivateTransactions(ethcore_private_tx::Error, ethcore_private_tx::ErrorKind); + } + + foreign_links { + Ethcore(ethcore::error::Error); + IoError(io::IoError); + } +} diff --git a/ethcore/service/src/lib.rs b/ethcore/service/src/lib.rs index 907009ba324a253aa7182922a85f1d854c929389..d85a377cde2ee28daccc52f797e0502912cc1eca 100644 --- a/ethcore/service/src/lib.rs +++ b/ethcore/service/src/lib.rs @@ -17,16 +17,28 @@ extern crate ansi_term; extern crate ethcore; extern crate ethcore_io as io; +extern crate ethcore_private_tx; +extern crate ethcore_sync as sync; extern crate kvdb; -extern crate kvdb_rocksdb; extern crate stop_guard; +#[macro_use] +extern crate error_chain; + #[macro_use] extern crate log; +#[macro_use] +extern crate trace_time; + #[cfg(test)] extern crate tempdir; +mod error; mod service; -pub use service::ClientService; +#[cfg(test)] +extern crate kvdb_rocksdb; + +pub use error::{Error, ErrorKind}; +pub use service::{ClientService, PrivateTxService}; diff --git a/ethcore/service/src/service.rs b/ethcore/service/src/service.rs index f190d6e6ac39e7d6cd6239970ed6feb3d3103209..7248d97229f24ce8b458b03eb32c2fc7176b2f9c 100644 --- a/ethcore/service/src/service.rs +++ b/ethcore/service/src/service.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -18,27 +18,58 @@ use std::sync::Arc; use std::path::Path; +use std::time::Duration; use ansi_term::Colour; use io::{IoContext, TimerToken, IoHandler, IoService, IoError}; -use kvdb::KeyValueDB; -use kvdb_rocksdb::{Database, DatabaseConfig}; +use kvdb::{KeyValueDB, KeyValueDBHandler}; use stop_guard::StopGuard; -use ethcore::client::{self, Client, ClientConfig, ChainNotify, ClientIoMessage}; -use ethcore::db; -use ethcore::error::Error; +use sync::PrivateTxHandler; +use ethcore::client::{Client, ClientConfig, ChainNotify, ClientIoMessage}; use ethcore::miner::Miner; use ethcore::snapshot::service::{Service as SnapshotService, ServiceParams as SnapServiceParams}; -use ethcore::snapshot::{RestorationStatus}; +use ethcore::snapshot::{SnapshotService as _SnapshotService, RestorationStatus}; use ethcore::spec::Spec; +use ethcore::account_provider::AccountProvider; + +use ethcore_private_tx::{self, Importer}; +use Error; + +pub struct PrivateTxService { + provider: Arc, +} + +impl PrivateTxService { + fn new(provider: Arc) -> Self { + PrivateTxService { + provider, + } + } + + /// Returns underlying provider. + pub fn provider(&self) -> Arc { + self.provider.clone() + } +} + +impl PrivateTxHandler for PrivateTxService { + fn import_private_transaction(&self, rlp: &[u8]) -> Result<(), String> { + self.provider.import_private_transaction(rlp).map_err(|e| e.to_string()) + } + + fn import_signed_private_transaction(&self, rlp: &[u8]) -> Result<(), String> { + self.provider.import_signed_private_transaction(rlp).map_err(|e| e.to_string()) + } +} /// Client service setup. Creates and registers client and network services with the IO subsystem. pub struct ClientService { io_service: Arc>, client: Arc, snapshot: Arc, - database: Arc, + private_tx: Arc, + database: Arc, _stop_guard: StopGuard, } @@ -47,35 +78,27 @@ impl ClientService { pub fn start( config: ClientConfig, spec: &Spec, - client_path: &Path, + client_db: Arc, snapshot_path: &Path, + restoration_db_handler: Box, _ipc_path: &Path, miner: Arc, + account_provider: Arc, + encryptor: Box, + private_tx_conf: ethcore_private_tx::ProviderConfig, ) -> Result { let io_service = IoService::::start()?; info!("Configured for {} using {} engine", Colour::White.bold().paint(spec.name.clone()), Colour::Yellow.bold().paint(spec.engine.name())); - let mut db_config = DatabaseConfig::with_columns(db::NUM_COLUMNS); - - db_config.memory_budget = config.db_cache_size; - db_config.compaction = config.db_compaction.compaction_profile(client_path); - db_config.wal = config.db_wal; - - let db = Arc::new(Database::open( - &db_config, - &client_path.to_str().expect("DB path could not be converted to string.") - ).map_err(client::Error::Database)?); - - let pruning = config.pruning; - let client = Client::new(config, &spec, db.clone(), miner, io_service.channel())?; + let client = Client::new(config, &spec, client_db.clone(), miner.clone(), io_service.channel())?; let snapshot_params = SnapServiceParams { engine: spec.engine.clone(), genesis_block: spec.genesis_block(), - db_config: db_config.clone(), + restoration_db_handler: restoration_db_handler, pruning: pruning, channel: io_service.channel(), snapshot_root: snapshot_path.into(), @@ -83,6 +106,16 @@ impl ClientService { }; let snapshot = Arc::new(SnapshotService::new(snapshot_params)?); + let provider = Arc::new(ethcore_private_tx::Provider::new( + client.clone(), + miner, + account_provider, + encryptor, + private_tx_conf, + io_service.channel(), + )); + let private_tx = Arc::new(PrivateTxService::new(provider)); + let client_io = Arc::new(ClientIoHandler { client: client.clone(), snapshot: snapshot.clone(), @@ -97,7 +130,8 @@ impl ClientService { io_service: Arc::new(io_service), client: client, snapshot: snapshot, - database: db, + private_tx, + database: client_db, _stop_guard: stop_guard, }) } @@ -117,6 +151,11 @@ impl ClientService { self.snapshot.clone() } + /// Get private transaction service. + pub fn private_tx_service(&self) -> Arc { + self.private_tx.clone() + } + /// Get network service component pub fn io(&self) -> Arc> { self.io_service.clone() @@ -129,6 +168,11 @@ impl ClientService { /// Get a handle to the database. pub fn db(&self) -> Arc { self.database.clone() } + + /// Shutdown the Client Service + pub fn shutdown(&self) { + self.snapshot.shutdown(); + } } /// IO interface for the Client handler @@ -140,16 +184,17 @@ struct ClientIoHandler { const CLIENT_TICK_TIMER: TimerToken = 0; const SNAPSHOT_TICK_TIMER: TimerToken = 1; -const CLIENT_TICK_MS: u64 = 5000; -const SNAPSHOT_TICK_MS: u64 = 10000; +const CLIENT_TICK: Duration = Duration::from_secs(5); +const SNAPSHOT_TICK: Duration = Duration::from_secs(10); impl IoHandler for ClientIoHandler { fn initialize(&self, io: &IoContext) { - io.register_timer(CLIENT_TICK_TIMER, CLIENT_TICK_MS).expect("Error registering client timer"); - io.register_timer(SNAPSHOT_TICK_TIMER, SNAPSHOT_TICK_MS).expect("Error registering snapshot timer"); + io.register_timer(CLIENT_TICK_TIMER, CLIENT_TICK).expect("Error registering client timer"); + io.register_timer(SNAPSHOT_TICK_TIMER, SNAPSHOT_TICK).expect("Error registering snapshot timer"); } fn timeout(&self, _io: &IoContext, timer: TimerToken) { + trace_time!("service::read"); match timer { CLIENT_TICK_TIMER => { use ethcore::snapshot::SnapshotService; @@ -162,20 +207,24 @@ impl IoHandler for ClientIoHandler { } fn message(&self, _io: &IoContext, net_message: &ClientIoMessage) { + trace_time!("service::message"); use std::thread; match *net_message { - ClientIoMessage::BlockVerified => { self.client.import_verified_blocks(); } - ClientIoMessage::NewTransactions(ref transactions, peer_id) => { - self.client.import_queued_transactions(transactions, peer_id); + ClientIoMessage::BlockVerified => { + self.client.import_verified_blocks(); } ClientIoMessage::BeginRestoration(ref manifest) => { if let Err(e) = self.snapshot.init_restore(manifest.clone(), true) { warn!("Failed to initialize snapshot restoration: {}", e); } } - ClientIoMessage::FeedStateChunk(ref hash, ref chunk) => self.snapshot.feed_state_chunk(*hash, chunk), - ClientIoMessage::FeedBlockChunk(ref hash, ref chunk) => self.snapshot.feed_block_chunk(*hash, chunk), + ClientIoMessage::FeedStateChunk(ref hash, ref chunk) => { + self.snapshot.feed_state_chunk(*hash, chunk) + } + ClientIoMessage::FeedBlockChunk(ref hash, ref chunk) => { + self.snapshot.feed_block_chunk(*hash, chunk) + } ClientIoMessage::TakeSnapshot(num) => { let client = self.client.clone(); let snapshot = self.snapshot.clone(); @@ -190,9 +239,9 @@ impl IoHandler for ClientIoHandler { debug!(target: "snapshot", "Failed to initialize periodic snapshot thread: {:?}", e); } }, - ClientIoMessage::NewMessage(ref message) => if let Err(e) = self.client.engine().handle_message(message) { - trace!(target: "poa", "Invalid message received: {}", e); - }, + ClientIoMessage::Execute(ref exec) => { + (*exec.0)(&self.client); + } _ => {} // ignore other messages } } @@ -205,25 +254,61 @@ mod tests { use tempdir::TempDir; + use ethcore::account_provider::AccountProvider; use ethcore::client::ClientConfig; use ethcore::miner::Miner; use ethcore::spec::Spec; + use ethcore::db::NUM_COLUMNS; + use kvdb::Error; + use kvdb_rocksdb::{Database, DatabaseConfig, CompactionProfile}; use super::*; + use ethcore_private_tx; + #[test] fn it_can_be_started() { let tempdir = TempDir::new("").unwrap(); let client_path = tempdir.path().join("client"); let snapshot_path = tempdir.path().join("snapshot"); + let client_config = ClientConfig::default(); + let mut client_db_config = DatabaseConfig::with_columns(NUM_COLUMNS); + + client_db_config.memory_budget = client_config.db_cache_size; + client_db_config.compaction = CompactionProfile::auto(&client_path); + client_db_config.wal = client_config.db_wal; + + let client_db = Arc::new(Database::open( + &client_db_config, + &client_path.to_str().expect("DB path could not be converted to string.") + ).unwrap()); + + struct RestorationDBHandler { + config: DatabaseConfig, + } + + impl KeyValueDBHandler for RestorationDBHandler { + fn open(&self, db_path: &Path) -> Result, Error> { + Ok(Arc::new(Database::open(&self.config, &db_path.to_string_lossy())?)) + } + } + + let restoration_db_handler = Box::new(RestorationDBHandler { + config: client_db_config, + }); + let spec = Spec::new_test(); let service = ClientService::start( ClientConfig::default(), &spec, - &client_path, + client_db, &snapshot_path, + restoration_db_handler, tempdir.path(), - Arc::new(Miner::with_spec(&spec)), + Arc::new(Miner::new_for_tests(&spec, None)), + Arc::new(AccountProvider::transient_provider()), + Box::new(ethcore_private_tx::NoopEncryptor), + Default::default(), ); assert!(service.is_ok()); drop(service.unwrap()); diff --git a/ethcore/src/account_db.rs b/ethcore/src/account_db.rs index 4e715766d7ae66202e25feb0c0db98e46605f63a..8fc4b95c6ee73a843d84e72a803b68ed5bf022f1 100644 --- a/ethcore/src/account_db.rs +++ b/ethcore/src/account_db.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/src/account_provider/mod.rs b/ethcore/src/account_provider/mod.rs old mode 100755 new mode 100644 index 74d24e7f50e95eb23c470bc7ac66e648412d851c..d05d3425db06211efb28d546b217bb53e42527fd --- a/ethcore/src/account_provider/mod.rs +++ b/ethcore/src/account_provider/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -41,7 +41,7 @@ pub use ethstore::{Derivation, IndexDerivation, KeyFile}; enum Unlock { /// If account is unlocked temporarily, it should be locked after first usage. OneTime, - /// Account unlocked permantently can always sign message. + /// Account unlocked permanently can always sign message. /// Use with caution. Perm, /// Account unlocked with a timeout @@ -66,8 +66,6 @@ pub enum SignError { Hardware(HardwareError), /// Low-level error from store SStore(SSError), - /// Inappropriate chain - InappropriateChain, } impl fmt::Display for SignError { @@ -77,7 +75,6 @@ impl fmt::Display for SignError { SignError::NotFound => write!(f, "Account does not exist"), SignError::Hardware(ref e) => write!(f, "{}", e), SignError::SStore(ref e) => write!(f, "{}", e), - SignError::InappropriateChain => write!(f, "Inappropriate chain"), } } } @@ -275,18 +272,18 @@ impl AccountProvider { } /// Checks whether an account with a given address is present. - pub fn has_account(&self, address: Address) -> Result { - Ok(self.sstore.account_ref(&address).is_ok() && !self.blacklisted_accounts.contains(&address)) + pub fn has_account(&self, address: Address) -> bool { + self.sstore.account_ref(&address).is_ok() && !self.blacklisted_accounts.contains(&address) } /// Returns addresses of all accounts. pub fn accounts(&self) -> Result, Error> { let accounts = self.sstore.accounts()?; Ok(accounts - .into_iter() - .map(|a| a.address) - .filter(|address| !self.blacklisted_accounts.contains(address)) - .collect() + .into_iter() + .map(|a| a.address) + .filter(|address| !self.blacklisted_accounts.contains(address)) + .collect() ) } @@ -498,7 +495,7 @@ impl AccountProvider { self.address_book.write().set_meta(account, meta) } - /// Removes and address from the addressbook + /// Removes and address from the address book pub fn remove_address(&self, addr: Address) { self.address_book.write().remove(addr) } @@ -588,7 +585,7 @@ impl AccountProvider { fn unlock_account(&self, address: Address, password: String, unlock: Unlock) -> Result<(), Error> { let account = self.sstore.account_ref(&address)?; - // check if account is already unlocked pernamently, if it is, do nothing + // 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 { @@ -641,8 +638,8 @@ impl AccountProvider { } /// Unlocks account temporarily with a timeout. - pub fn unlock_account_timed(&self, account: Address, password: String, duration_ms: u32) -> Result<(), Error> { - self.unlock_account(account, password, Unlock::Timed(Instant::now() + Duration::from_millis(duration_ms as u64))) + pub fn unlock_account_timed(&self, account: Address, password: String, duration: Duration) -> Result<(), Error> { + self.unlock_account(account, password, Unlock::Timed(Instant::now() + duration)) } /// Checks if given account is unlocked @@ -812,8 +809,17 @@ impl AccountProvider { .map_err(Into::into) } + /// Sign message with hardware wallet. + pub fn sign_message_with_hardware(&self, address: &Address, message: &[u8]) -> Result { + match self.hardware_store.as_ref().map(|s| s.sign_message(address, message)) { + None | Some(Err(HardwareError::KeyNotFound)) => Err(SignError::NotFound), + Some(Err(e)) => Err(From::from(e)), + Some(Ok(s)) => Ok(s), + } + } + /// Sign transaction with hardware wallet. - pub fn sign_with_hardware(&self, address: Address, transaction: &Transaction, chain_id: Option, rlp_encoded_transaction: &[u8]) -> Result { + pub fn sign_transaction_with_hardware(&self, address: &Address, transaction: &Transaction, chain_id: Option, rlp_encoded_transaction: &[u8]) -> Result { let t_info = TransactionInfo { nonce: transaction.nonce, gas_price: transaction.gas_price, @@ -837,7 +843,7 @@ impl AccountProvider { #[cfg(test)] mod tests { use super::{AccountProvider, Unlock, DappId}; - use std::time::Instant; + use std::time::{Duration, Instant}; use ethstore::ethkey::{Generator, Random, Address}; use ethstore::{StoreAccountRef, Derivation}; use ethereum_types::H256; @@ -941,8 +947,8 @@ mod tests { let kp = Random.generate().unwrap(); let ap = AccountProvider::transient_provider(); assert!(ap.insert_account(kp.secret().clone(), "test").is_ok()); - assert!(ap.unlock_account_timed(kp.address(), "test1".into(), 60000).is_err()); - assert!(ap.unlock_account_timed(kp.address(), "test".into(), 60000).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()); diff --git a/ethcore/src/account_provider/stores.rs b/ethcore/src/account_provider/stores.rs index 1563d21bc6716384fbd2fedb42d4276f651cc688..d7725deb7e35858a1c994a1ddbc90a0dec0cc4a5 100644 --- a/ethcore/src/account_provider/stores.rs +++ b/ethcore/src/account_provider/stores.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/src/block.rs b/ethcore/src/block.rs index a0318d3e187a3126878200b1e12c9f52dc3b7c37..682171170e1935a19cd5e367403a994c30aba7f3 100644 --- a/ethcore/src/block.rs +++ b/ethcore/src/block.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -22,7 +22,7 @@ use std::collections::HashSet; use hash::{keccak, KECCAK_NULL_RLP, KECCAK_EMPTY_LIST_RLP}; use triehash::ordered_trie_root; -use rlp::{UntrustedRlp, RlpStream, Encodable, Decodable, DecoderError, encode_list}; +use rlp::{Rlp, RlpStream, Encodable, Decodable, DecoderError, encode_list}; use ethereum_types::{H256, U256, Address, Bloom}; use bytes::Bytes; use unexpected::{Mismatch, OutOfBounds}; @@ -31,7 +31,7 @@ use vm::{EnvInfo, LastHashes}; use engines::EthEngine; use error::{Error, BlockError}; use factory::Factories; -use header::{Header, Seal}; +use header::{Header, ExtendedHeader}; use receipt::{Receipt, TransactionOutcome}; use state::State; use state_db::StateDB; @@ -54,13 +54,13 @@ pub struct Block { impl Block { /// Returns true if the given bytes form a valid encoding of a block in RLP. pub fn is_good(b: &[u8]) -> bool { - UntrustedRlp::new(b).as_val::().is_ok() + Rlp::new(b).as_val::().is_ok() } - /// Get the RLP-encoding of the block with or without the seal. - pub fn rlp_bytes(&self, seal: Seal) -> Bytes { + /// Get the RLP-encoding of the block with the seal. + pub fn rlp_bytes(&self) -> Bytes { let mut block_rlp = RlpStream::new_list(3); - self.header.stream_rlp(&mut block_rlp, seal); + block_rlp.append(&self.header); block_rlp.append_list(&self.transactions); block_rlp.append_list(&self.uncles); block_rlp.out() @@ -68,7 +68,7 @@ impl Block { } impl Decodable for Block { - fn decode(rlp: &UntrustedRlp) -> Result { + fn decode(rlp: &Rlp) -> Result { if rlp.as_raw().len() != rlp.payload_info()?.total() { return Err(DecoderError::RlpIsTooBig); } @@ -94,6 +94,8 @@ pub struct ExecutedBlock { state: State, traces: Tracing, last_hashes: Arc, + is_finalized: bool, + metadata: Option>, } impl ExecutedBlock { @@ -112,6 +114,8 @@ impl ExecutedBlock { Tracing::Disabled }, last_hashes: last_hashes, + is_finalized: false, + metadata: None, } } @@ -206,6 +210,26 @@ impl ::parity_machine::Transactions for ExecutedBlock { } } +impl ::parity_machine::Finalizable for ExecutedBlock { + fn is_finalized(&self) -> bool { + self.is_finalized + } + + fn mark_finalized(&mut self) { + self.is_finalized = true; + } +} + +impl ::parity_machine::WithMetadata for ExecutedBlock { + fn metadata(&self) -> Option<&[u8]> { + self.metadata.as_ref().map(|v| v.as_ref()) + } + + fn set_metadata(&mut self, value: Option>) { + self.metadata = value; + } +} + /// Block that is ready for transactions to be added. /// /// It's a bit like a Vec, except that whenever a transaction is pushed, we execute it and @@ -224,6 +248,8 @@ pub struct ClosedBlock { block: ExecutedBlock, uncle_bytes: Bytes, unclosed_state: State, + unclosed_finalization_state: bool, + unclosed_metadata: Option>, } /// Just like `ClosedBlock` except that we can't reopen it and it's faster. @@ -245,7 +271,7 @@ pub struct SealedBlock { impl<'x> OpenBlock<'x> { /// Create a new `OpenBlock` ready for transaction pushing. - pub fn new( + pub fn new<'a>( engine: &'x EthEngine, factories: Factories, tracing: bool, @@ -256,6 +282,7 @@ impl<'x> OpenBlock<'x> { gas_range_target: (U256, U256), extra_data: Bytes, is_epoch_begin: bool, + ancestry: &mut Iterator, ) -> Result { let number = parent.number() + 1; let state = State::from_existing(db, parent.state_root().clone(), engine.account_start_nonce(number), factories)?; @@ -267,9 +294,8 @@ impl<'x> OpenBlock<'x> { r.block.header.set_parent_hash(parent.hash()); r.block.header.set_number(number); r.block.header.set_author(author); - r.block.header.set_timestamp_now(parent.timestamp()); + r.block.header.set_timestamp(engine.open_block_header_timestamp(parent.timestamp())); r.block.header.set_extra_data(extra_data); - r.block.header.note_dirty(); let gas_floor_target = cmp::max(gas_range_target.0, engine.params().min_gas_limit); let gas_ceil_target = cmp::max(gas_range_target.1, gas_floor_target); @@ -278,13 +304,15 @@ impl<'x> OpenBlock<'x> { engine.populate_from_parent(&mut r.block.header, parent); engine.machine().on_new_block(&mut r.block)?; - engine.on_new_block(&mut r.block, is_epoch_begin)?; + engine.on_new_block(&mut r.block, is_epoch_begin, ancestry)?; Ok(r) } /// Alter the timestamp of the block. - pub fn set_timestamp(&mut self, timestamp: u64) { self.block.header.set_timestamp(timestamp); } + pub fn set_timestamp(&mut self, timestamp: u64) { + self.block.header.set_timestamp(timestamp); + } /// Removes block gas limit. pub fn remove_gas_limit(&mut self) { @@ -336,8 +364,33 @@ impl<'x> OpenBlock<'x> { } /// Push transactions onto the block. - pub fn push_transactions(&mut self, transactions: &[SignedTransaction]) -> Result<(), Error> { - push_transactions(self, transactions) + #[cfg(not(feature = "slow-blocks"))] + fn push_transactions(&mut self, transactions: Vec) -> Result<(), Error> { + for t in transactions { + self.push_transaction(t, None)?; + } + Ok(()) + } + + /// Push transactions onto the block. + #[cfg(feature = "slow-blocks")] + fn push_transactions(&mut self, transactions: Vec) -> Result<(), Error> { + use std::time; + + let slow_tx = option_env!("SLOW_TX_DURATION").and_then(|v| v.parse().ok()).unwrap_or(100); + for t in transactions { + let hash = t.hash(); + let start = time::Instant::now(); + self.push_transaction(t, None)?; + let took = start.elapsed(); + let took_ms = took.as_secs() * 1000 + took.subsec_nanos() as u64 / 1000000; + if took > time::Duration::from_millis(slow_tx) { + warn!("Heavy ({} ms) transaction in block {:?}: {:?}", took_ms, block.header().number(), hash); + } + debug!(target: "tx", "Transaction {:?} took: {} ms", hash, took_ms); + } + + Ok(()) } /// Populate self from a header. @@ -361,6 +414,8 @@ impl<'x> OpenBlock<'x> { let mut s = self; let unclosed_state = s.block.state.clone(); + let unclosed_metadata = s.block.metadata.clone(); + let unclosed_finalization_state = s.block.is_finalized; if let Err(e) = s.engine.on_close_block(&mut s.block) { warn!("Encountered error on closing the block: {}", e); @@ -374,13 +429,18 @@ impl<'x> OpenBlock<'x> { s.block.header.set_uncles_hash(keccak(&uncle_bytes)); s.block.header.set_state_root(s.block.state.root().clone()); s.block.header.set_receipts_root(ordered_trie_root(s.block.receipts.iter().map(|r| r.rlp_bytes()))); - s.block.header.set_log_bloom(s.block.receipts.iter().fold(Bloom::zero(), |mut b, r| {b = &b | &r.log_bloom; b})); //TODO: use |= operator - s.block.header.set_gas_used(s.block.receipts.last().map_or(U256::zero(), |r| r.gas_used)); + s.block.header.set_log_bloom(s.block.receipts.iter().fold(Bloom::zero(), |mut b, r| { + b.accrue_bloom(&r.log_bloom); + b + })); + s.block.header.set_gas_used(s.block.receipts.last().map_or_else(U256::zero, |r| r.gas_used)); ClosedBlock { block: s.block, uncle_bytes, unclosed_state, + unclosed_metadata, + unclosed_finalization_state, } } @@ -395,6 +455,7 @@ impl<'x> OpenBlock<'x> { if let Err(e) = s.block.state.commit() { warn!("Encountered error on state commit: {}", e); } + if s.block.header.transactions_root().is_zero() || s.block.header.transactions_root() == &KECCAK_NULL_RLP { s.block.header.set_transactions_root(ordered_trie_root(s.block.transactions.iter().map(|e| e.rlp_bytes()))); } @@ -407,8 +468,11 @@ impl<'x> OpenBlock<'x> { } s.block.header.set_state_root(s.block.state.root().clone()); - s.block.header.set_log_bloom(s.block.receipts.iter().fold(Bloom::zero(), |mut b, r| {b = &b | &r.log_bloom; b})); //TODO: use |= operator - s.block.header.set_gas_used(s.block.receipts.last().map_or(U256::zero(), |r| r.gas_used)); + s.block.header.set_log_bloom(s.block.receipts.iter().fold(Bloom::zero(), |mut b, r| { + b.accrue_bloom(&r.log_bloom); + b + })); + s.block.header.set_gas_used(s.block.receipts.last().map_or_else(U256::zero, |r| r.gas_used)); LockedBlock { block: s.block, @@ -435,7 +499,7 @@ impl<'x> IsBlock for LockedBlock { impl ClosedBlock { /// Get the hash of the header without seal arguments. - pub fn hash(&self) -> H256 { self.header().rlp_keccak(Seal::Without) } + pub fn hash(&self) -> H256 { self.header().bare_hash() } /// Turn this into a `LockedBlock`, unable to be reopened again. pub fn lock(self) -> LockedBlock { @@ -450,6 +514,8 @@ impl ClosedBlock { // revert rewards (i.e. set state back at last transaction's state). let mut block = self.block; block.state = self.unclosed_state; + block.metadata = self.unclosed_metadata; + block.is_finalized = self.unclosed_finalization_state; OpenBlock { block: block, engine: engine, @@ -458,8 +524,26 @@ impl ClosedBlock { } impl LockedBlock { + + /// Removes outcomes from receipts and updates the receipt root. + /// + /// This is done after the block is enacted for historical reasons. + /// We allow inconsistency in receipts for some chains if `validate_receipts_transition` + /// is set to non-zero value, so the check only happens if we detect + /// unmatching root first and then fall back to striped receipts. + pub fn strip_receipts_outcomes(&mut self) { + for receipt in &mut self.block.receipts { + receipt.outcome = TransactionOutcome::Unknown; + } + self.block.header.set_receipts_root( + ordered_trie_root(self.block.receipts.iter().map(|r| r.rlp_bytes())) + ); + // compute hash and cache it. + self.block.header.compute_hash(); + } + /// Get the hash of the header without seal arguments. - pub fn hash(&self) -> H256 { self.header().rlp_keccak(Seal::Without) } + pub fn hash(&self) -> H256 { self.header().bare_hash() } /// Provide a valid seal in order to turn this into a `SealedBlock`. /// @@ -472,6 +556,7 @@ impl LockedBlock { Mismatch { expected: expected_seal_fields, found: seal.len() })); } s.block.header.set_seal(seal); + s.block.header.compute_hash(); Ok(SealedBlock { block: s.block, uncle_bytes: s.uncle_bytes }) } @@ -485,6 +570,7 @@ impl LockedBlock { ) -> Result { let mut s = self; s.block.header.set_seal(seal); + s.block.header.compute_hash(); // TODO: passing state context to avoid engines owning it? match engine.verify_local_seal(&s.block.header) { @@ -492,16 +578,6 @@ impl LockedBlock { _ => Ok(SealedBlock { block: s.block, uncle_bytes: s.uncle_bytes }), } } - - /// Remove state root from transaction receipts to make them EIP-98 compatible. - pub fn strip_receipts(self) -> LockedBlock { - let mut block = self; - for receipt in &mut block.block.receipts { - receipt.outcome = TransactionOutcome::Unknown; - } - block.block.header.set_receipts_root(ordered_trie_root(block.block.receipts.iter().map(|r| r.rlp_bytes()))); - block - } } impl Drain for LockedBlock { @@ -515,7 +591,7 @@ impl SealedBlock { /// Get the RLP-encoding of the block. pub fn rlp_bytes(&self) -> Bytes { let mut block_rlp = RlpStream::new_list(3); - self.block.header.stream_rlp(&mut block_rlp, Seal::With); + block_rlp.append(&self.block.header); block_rlp.append_list(&self.block.transactions); block_rlp.append_raw(&self.uncle_bytes, 1); block_rlp.out() @@ -534,10 +610,10 @@ impl IsBlock for SealedBlock { } /// Enact the block given by block header, transactions and uncles -pub fn enact( - header: &Header, - transactions: &[SignedTransaction], - uncles: &[Header], +fn enact( + header: Header, + transactions: Vec, + uncles: Vec
, engine: &EthEngine, tracing: bool, db: StateDB, @@ -545,6 +621,7 @@ pub fn enact( last_hashes: Arc, factories: Factories, is_epoch_begin: bool, + ancestry: &mut Iterator, ) -> Result { { if ::log::max_log_level() >= ::log::LogLevel::Trace { @@ -565,50 +642,22 @@ pub fn enact( (3141562.into(), 31415620.into()), vec![], is_epoch_begin, + ancestry, )?; - b.populate_from(header); + b.populate_from(&header); b.push_transactions(transactions)?; for u in uncles { - b.push_uncle(u.clone())?; + b.push_uncle(u)?; } Ok(b.close_and_lock()) } -#[inline] -#[cfg(not(feature = "slow-blocks"))] -fn push_transactions(block: &mut OpenBlock, transactions: &[SignedTransaction]) -> Result<(), Error> { - for t in transactions { - block.push_transaction(t.clone(), None)?; - } - Ok(()) -} - -#[cfg(feature = "slow-blocks")] -fn push_transactions(block: &mut OpenBlock, transactions: &[SignedTransaction]) -> Result<(), Error> { - use std::time; - - let slow_tx = option_env!("SLOW_TX_DURATION").and_then(|v| v.parse().ok()).unwrap_or(100); - for t in transactions { - let hash = t.hash(); - let start = time::Instant::now(); - block.push_transaction(t.clone(), None)?; - let took = start.elapsed(); - let took_ms = took.as_secs() * 1000 + took.subsec_nanos() as u64 / 1000000; - if took > time::Duration::from_millis(slow_tx) { - warn!("Heavy ({} ms) transaction in block {:?}: {:?}", took_ms, block.header().number(), hash); - } - debug!(target: "tx", "Transaction {:?} took: {} ms", hash, took_ms); - } - Ok(()) -} - -// TODO [ToDr] Pass `PreverifiedBlock` by move, this will avoid unecessary allocation /// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header pub fn enact_verified( - block: &PreverifiedBlock, + block: PreverifiedBlock, engine: &EthEngine, tracing: bool, db: StateDB, @@ -616,13 +665,14 @@ pub fn enact_verified( last_hashes: Arc, factories: Factories, is_epoch_begin: bool, + ancestry: &mut Iterator, ) -> Result { - let view = BlockView::new(&block.bytes); + let view = view!(BlockView, &block.bytes); enact( - &block.header, - &block.transactions, - &view.uncles(), + block.header, + block.transactions, + view.uncles(), engine, tracing, db, @@ -630,12 +680,13 @@ pub fn enact_verified( last_hashes, factories, is_epoch_begin, + ancestry, ) } #[cfg(test)] mod tests { - use tests::helpers::get_temp_state_db; + use test_helpers::get_temp_state_db; use super::*; use engines::EthEngine; use vm::LastHashes; @@ -658,7 +709,7 @@ mod tests { last_hashes: Arc, factories: Factories, ) -> Result { - let block = BlockView::new(block_bytes); + let block = view!(BlockView, block_bytes); let header = block.header(); let transactions: Result, Error> = block .transactions() @@ -687,10 +738,11 @@ mod tests { (3141562.into(), 31415620.into()), vec![], false, + &mut Vec::new().into_iter(), )?; b.populate_from(&header); - b.push_transactions(&transactions)?; + b.push_transactions(transactions)?; for u in &block.uncles() { b.push_uncle(u.clone())?; @@ -709,7 +761,7 @@ mod tests { last_hashes: Arc, factories: Factories, ) -> Result { - let header = BlockView::new(block_bytes).header_view(); + let header = view!(BlockView, block_bytes).header_view(); Ok(enact_bytes(block_bytes, engine, tracing, db, parent, last_hashes, factories)?.seal(engine, header.seal())?) } @@ -720,7 +772,7 @@ mod tests { let genesis_header = spec.genesis_header(); let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); let last_hashes = Arc::new(vec![genesis_header.hash()]); - let b = OpenBlock::new(&*spec.engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![], false).unwrap(); + let b = OpenBlock::new(&*spec.engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![], false, &mut Vec::new().into_iter()).unwrap(); let b = b.close_and_lock(); let _ = b.seal(&*spec.engine, vec![]); } @@ -734,7 +786,7 @@ mod tests { let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); let last_hashes = Arc::new(vec![genesis_header.hash()]); - let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes.clone(), Address::zero(), (3141562.into(), 31415620.into()), vec![], false).unwrap() + let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes.clone(), Address::zero(), (3141562.into(), 31415620.into()), vec![], false, &mut Vec::new().into_iter()).unwrap() .close_and_lock().seal(engine, vec![]).unwrap(); let orig_bytes = b.rlp_bytes(); let orig_db = b.drain(); @@ -758,7 +810,7 @@ mod tests { let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); let last_hashes = Arc::new(vec![genesis_header.hash()]); - let mut open_block = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes.clone(), Address::zero(), (3141562.into(), 31415620.into()), vec![], false).unwrap(); + let mut open_block = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes.clone(), Address::zero(), (3141562.into(), 31415620.into()), vec![], false, &mut Vec::new().into_iter()).unwrap(); let mut uncle1_header = Header::new(); uncle1_header.set_extra_data(b"uncle1".to_vec()); let mut uncle2_header = Header::new(); @@ -775,7 +827,7 @@ mod tests { let bytes = e.rlp_bytes(); assert_eq!(bytes, orig_bytes); - let uncles = BlockView::new(&bytes).uncles(); + let uncles = view!(BlockView, &bytes).uncles(); assert_eq!(uncles[1].extra_data(), b"uncle2"); let db = e.drain(); diff --git a/ethcore/src/blockchain/best_block.rs b/ethcore/src/blockchain/best_block.rs index 1436ced275de9b1017aa30ce6a91536d59b49c50..adfaf68aadd8514d9627a2474a6294645ee60993 100644 --- a/ethcore/src/blockchain/best_block.rs +++ b/ethcore/src/blockchain/best_block.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -15,8 +15,9 @@ // along with Parity. If not, see . use ethereum_types::{H256, U256}; -use bytes::Bytes; -use header::BlockNumber; + +use encoded; +use header::{Header, BlockNumber}; /// Contains information on a best block that is specific to the consensus engine. /// @@ -24,18 +25,13 @@ use header::BlockNumber; /// combined difficulty (usually the block with the highest block number). /// /// Sometimes refered as 'latest block'. -#[derive(Default)] pub struct BestBlock { - /// Best block hash. - pub hash: H256, - /// Best block number. - pub number: BlockNumber, - /// Best block timestamp. - pub timestamp: u64, + /// Best block decoded header. + pub header: Header, + /// Best block uncompressed bytes. + pub block: encoded::Block, /// Best block total difficulty. pub total_difficulty: U256, - /// Best block uncompressed bytes - pub block: Bytes, } /// Best ancient block info. If the blockchain has a gap this keeps track of where it starts. diff --git a/ethcore/src/blockchain/block_info.rs b/ethcore/src/blockchain/block_info.rs index ee8a50d09d9eee44fc4483447b581b7089de1f6a..6a48e9244730e344c78ed62ba81ffcb650ed4b49 100644 --- a/ethcore/src/blockchain/block_info.rs +++ b/ethcore/src/blockchain/block_info.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/src/blockchain/blockchain.rs b/ethcore/src/blockchain/blockchain.rs index 09be1143f2ec46ad6c785b24c868e2cf7b00b604..ee781ebe5340abe2af83adf41475f4b3cbc2151e 100644 --- a/ethcore/src/blockchain/blockchain.rs +++ b/ethcore/src/blockchain/blockchain.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -25,11 +25,11 @@ use heapsize::HeapSizeOf; use ethereum_types::{H256, Bloom, U256}; use parking_lot::{Mutex, RwLock}; use bytes::Bytes; -use rlp::*; +use rlp::RlpStream; use rlp_compress::{compress, decompress, blocks_swapper}; use header::*; use transaction::*; -use views::*; +use views::{BlockView, HeaderView}; use log_entry::{LogEntry, LocalizedLogEntry}; use receipt::Receipt; use blooms::{BloomGroup, GroupPosition}; @@ -38,11 +38,12 @@ use blockchain::block_info::{BlockInfo, BlockLocation, BranchBecomingCanonChainD use blockchain::extras::{BlockReceipts, BlockDetails, TransactionAddress, EPOCH_KEY_PREFIX, EpochTransitions}; use types::blockchain_info::BlockChainInfo; use types::tree_route::TreeRoute; -use blockchain::update::ExtrasUpdate; +use blockchain::update::{ExtrasUpdate, ExtrasInsert}; use blockchain::{CacheSize, ImportRoute, Config}; use db::{self, Writable, Readable, CacheUpdatePolicy}; use cache_manager::CacheManager; use encoded; +use engines::ForkChoice; use engines::epoch::{Transition as EpochTransition, PendingTransition as PendingEpochTransition}; use rayon::prelude::*; use ansi_term::Colour; @@ -57,6 +58,12 @@ pub trait BlockProvider { /// (though not necessarily a part of the canon chain). fn is_known(&self, hash: &H256) -> bool; + /// Returns true if the given block is known and in the canon chain. + fn is_canon(&self, hash: &H256) -> bool { + let is_canon = || Some(hash == &self.block_hash(self.block_number(hash)?)?); + is_canon().unwrap_or(false) + } + /// Get the first block of the best part of the chain. /// Return `None` if there is no gap and the first block is the genesis. /// Any queries of blocks which precede this one are not guaranteed to @@ -90,11 +97,6 @@ pub trait BlockProvider { /// Get receipts of block with given hash. fn block_receipts(&self, hash: &H256) -> Option; - /// Get the partial-header of a block. - fn block_header(&self, hash: &H256) -> Option
{ - self.block_header_data(hash).map(|header| header.decode()) - } - /// Get the header RLP of a block. fn block_header_data(&self, hash: &H256) -> Option; @@ -115,7 +117,7 @@ pub trait BlockProvider { /// Get the number of given block's hash. fn block_number(&self, hash: &H256) -> Option { - self.block_details(hash).map(|details| details.number) + self.block_header_data(hash).map(|header| header.number()) } /// Get transaction with given transaction hash. @@ -144,8 +146,8 @@ pub trait BlockProvider { } /// Returns the header of the genesis block. - fn genesis_header(&self) -> Header { - self.block_header(&self.genesis_hash()) + fn genesis_header(&self) -> encoded::Header { + self.block_header_data(&self.genesis_hash()) .expect("Genesis header always stored; qed") } @@ -153,7 +155,7 @@ pub trait BlockProvider { fn blocks_with_bloom(&self, bloom: &Bloom, from_block: BlockNumber, to_block: BlockNumber) -> Vec; /// Returns logs matching given filter. - fn logs(&self, blocks: Vec, matches: F, limit: Option) -> Vec + fn logs(&self, blocks: Vec, matches: F, limit: Option) -> Vec where F: Fn(&LogEntry) -> bool + Send + Sync, Self: Sized; } @@ -193,8 +195,8 @@ pub struct BlockChain { first_block: Option, // block cache - block_headers: RwLock>, - block_bodies: RwLock>, + block_headers: RwLock>, + block_bodies: RwLock>, // extra caches block_details: RwLock>, @@ -236,13 +238,7 @@ impl BlockProvider for BlockChain { fn block(&self, hash: &H256) -> Option { let header = self.block_header_data(hash)?; let body = self.block_body(hash)?; - - let mut block = RlpStream::new_list(3); - let body_rlp = body.rlp(); - block.append_raw(header.rlp().as_raw(), 1); - block.append_raw(body_rlp.at(0).as_raw(), 1); - block.append_raw(body_rlp.at(1).as_raw(), 1); - Some(encoded::Block::new(block.out())) + Some(encoded::Block::new_from_header_and_body(&header.view(), &body.view())) } /// Get block header data @@ -251,17 +247,15 @@ impl BlockProvider for BlockChain { { let read = self.block_headers.read(); if let Some(v) = read.get(hash) { - return Some(encoded::Header::new(v.clone())); + return Some(v.clone()); } } // Check if it's the best block { let best_block = self.best_block.read(); - if &best_block.hash == hash { - return Some(encoded::Header::new( - Rlp::new(&best_block.block).at(0).as_raw().to_vec() - )) + if &best_block.header.hash() == hash { + return Some(best_block.header.encoded()) } } @@ -269,12 +263,12 @@ impl BlockProvider for BlockChain { let b = self.db.get(db::COL_HEADERS, hash) .expect("Low level database error. Some issue with disk?")?; - let bytes = decompress(&b, blocks_swapper()).into_vec(); + let header = encoded::Header::new(decompress(&b, blocks_swapper()).into_vec()); let mut write = self.block_headers.write(); - write.insert(*hash, bytes.clone()); + write.insert(*hash, header.clone()); self.cache_man.lock().note_used(CacheId::BlockHeader(*hash)); - Some(encoded::Header::new(bytes)) + Some(header) } /// Get block body data @@ -283,15 +277,15 @@ impl BlockProvider for BlockChain { { let read = self.block_bodies.read(); if let Some(v) = read.get(hash) { - return Some(encoded::Body::new(v.clone())); + return Some(v.clone()); } } // Check if it's the best block { let best_block = self.best_block.read(); - if &best_block.hash == hash { - return Some(encoded::Body::new(Self::block_to_body(&best_block.block))); + if &best_block.header.hash() == hash { + return Some(encoded::Body::new(Self::block_to_body(best_block.block.rlp().as_raw()))); } } @@ -299,12 +293,12 @@ impl BlockProvider for BlockChain { let b = self.db.get(db::COL_BODIES, hash) .expect("Low level database error. Some issue with disk?")?; - let bytes = decompress(&b, blocks_swapper()).into_vec(); + let body = encoded::Body::new(decompress(&b, blocks_swapper()).into_vec()); let mut write = self.block_bodies.write(); - write.insert(*hash, bytes.clone()); + write.insert(*hash, body.clone()); self.cache_man.lock().note_used(CacheId::BlockBody(*hash)); - Some(encoded::Body::new(bytes)) + Some(body) } /// Get the familial details concerning a block. @@ -345,16 +339,18 @@ impl BlockProvider for BlockChain { .collect() } - fn logs(&self, mut blocks: Vec, matches: F, limit: Option) -> Vec + /// Returns logs matching given filter. The order of logs returned will be the same as the order of the blocks + /// provided. And it's the callers responsibility to sort blocks provided in advance. + fn logs(&self, mut blocks: Vec, matches: F, limit: Option) -> Vec where F: Fn(&LogEntry) -> bool + Send + Sync, Self: Sized { // sort in reverse order - blocks.sort_by(|a, b| b.cmp(a)); + blocks.reverse(); let mut logs = blocks .chunks(128) .flat_map(move |blocks_chunk| { blocks_chunk.into_par_iter() - .filter_map(|number| self.block_hash(*number).map(|hash| (*number, hash))) + .filter_map(|hash| self.block_number(&hash).map(|r| (r, hash))) .filter_map(|(number, hash)| self.block_receipts(&hash).map(|r| (number, hash, r.receipts))) .filter_map(|(number, hash, receipts)| self.block_body(&hash).map(|ref b| (number, hash, receipts, b.transaction_hashes()))) .flat_map(|(number, hash, mut receipts, mut hashes)| { @@ -381,7 +377,7 @@ impl BlockProvider for BlockChain { .enumerate() .map(move |(i, log)| LocalizedLogEntry { entry: log, - block_hash: hash, + block_hash: *hash, block_number: number, transaction_hash: tx_hash, // iterating in reverse order @@ -422,6 +418,42 @@ impl<'a> Iterator for AncestryIter<'a> { } } +/// An iterator which walks the blockchain towards the genesis, with metadata information. +pub struct AncestryWithMetadataIter<'a> { + current: H256, + chain: &'a BlockChain, +} + +impl<'a> Iterator for AncestryWithMetadataIter<'a> { + type Item = ExtendedHeader; + fn next(&mut self) -> Option { + if self.current.is_zero() { + None + } else { + let details = self.chain.block_details(&self.current); + let header = self.chain.block_header_data(&self.current) + .map(|h| h.decode().expect("Stored block header data is valid RLP; qed")); + + match (details, header) { + (Some(details), Some(header)) => { + self.current = details.parent; + Some(ExtendedHeader { + parent_total_difficulty: details.total_difficulty - *header.difficulty(), + is_finalized: details.is_finalized, + metadata: details.metadata, + + header: header, + }) + }, + _ => { + self.current = H256::default(); + None + }, + } + } + } +} + /// An iterator which walks all epoch transitions. /// Returns epoch transitions. pub struct EpochTransitionIter<'a> { @@ -443,7 +475,7 @@ impl<'a> Iterator for EpochTransitionIter<'a> { return None } - let transitions: EpochTransitions = ::rlp::decode(&val[..]); + let transitions: EpochTransitions = ::rlp::decode(&val[..]).expect("decode error: the db is corrupted or the data structure has changed"); // if there are multiple candidates, at most one will be on the // canon chain. @@ -467,7 +499,7 @@ impl<'a> Iterator for EpochTransitionIter<'a> { impl BlockChain { /// Create new instance of blockchain from given Genesis. pub fn new(config: Config, genesis: &[u8], db: Arc) -> BlockChain { - // 400 is the avarage size of the key + // 400 is the average size of the key let cache_man = CacheManager::new(config.pref_cache_size, config.max_cache_size, 400); let mut bc = BlockChain { @@ -476,7 +508,12 @@ impl BlockChain { elements_per_index: LOG_BLOOMS_ELEMENTS_PER_INDEX, }, first_block: None, - best_block: RwLock::new(BestBlock::default()), + best_block: RwLock::new(BestBlock { + // BestBlock will be overwritten anyway. + header: Default::default(), + total_difficulty: Default::default(), + block: encoded::Block::new(genesis.into()), + }), best_ancient_block: RwLock::new(None), block_headers: RwLock::new(HashMap::new()), block_bodies: RwLock::new(HashMap::new()), @@ -501,7 +538,7 @@ impl BlockChain { None => { // best block does not exist // we need to insert genesis into the cache - let block = BlockView::new(genesis); + let block = view!(BlockView, genesis); let header = block.header_view(); let hash = block.hash(); @@ -510,6 +547,8 @@ impl BlockChain { total_difficulty: header.difficulty(), parent: header.parent_hash(), children: vec![], + is_finalized: false, + metadata: None, }; let mut batch = DBTransaction::new(); @@ -527,11 +566,21 @@ impl BlockChain { { // Fetch best block details - let best_block_number = bc.block_number(&best_block_hash).unwrap(); let best_block_total_difficulty = bc.block_details(&best_block_hash).unwrap().total_difficulty; - let best_block_rlp = bc.block(&best_block_hash).unwrap().into_inner(); - let best_block_timestamp = BlockView::new(&best_block_rlp).header().timestamp(); + let best_block_rlp = bc.block(&best_block_hash).unwrap(); + // and write them + let mut best_block = bc.best_block.write(); + *best_block = BestBlock { + total_difficulty: best_block_total_difficulty, + header: best_block_rlp.decode_header(), + block: best_block_rlp, + }; + } + + { + let best_block_number = bc.best_block.read().header.number(); + // Fetch first and best ancient block details let raw_first = bc.db.get(db::COL_EXTRA, b"first").unwrap().map(|v| v.into_vec()); let mut best_ancient = bc.db.get(db::COL_EXTRA, b"ancient").unwrap().map(|h| H256::from_slice(&h)); let best_ancient_number; @@ -574,15 +623,6 @@ impl BlockChain { } // and write them - let mut best_block = bc.best_block.write(); - *best_block = BestBlock { - number: best_block_number, - total_difficulty: best_block_total_difficulty, - hash: best_block_hash, - timestamp: best_block_timestamp, - block: best_block_rlp, - }; - if let (Some(hash), Some(number)) = (best_ancient, best_ancient_number) { let mut best_ancient_block = bc.best_ancient_block.write(); *best_ancient_block = Some(BestAncientBlock { @@ -648,6 +688,7 @@ impl BlockChain { /// `None` is returned. pub fn tree_route(&self, from: H256, to: H256) -> Option { let mut from_branch = vec![]; + let mut is_from_route_finalized = false; let mut to_branch = vec![]; let mut from_details = self.block_details(&from)?; @@ -660,6 +701,7 @@ impl BlockChain { from_branch.push(current_from); current_from = from_details.parent.clone(); from_details = self.block_details(&from_details.parent)?; + is_from_route_finalized = is_from_route_finalized || from_details.is_finalized; } while to_details.number > from_details.number { @@ -675,6 +717,7 @@ impl BlockChain { from_branch.push(current_from); current_from = from_details.parent.clone(); from_details = self.block_details(&from_details.parent)?; + is_from_route_finalized = is_from_route_finalized || from_details.is_finalized; to_branch.push(current_to); current_to = to_details.parent.clone(); @@ -688,7 +731,8 @@ impl BlockChain { Some(TreeRoute { blocks: from_branch, ancestor: current_from, - index: index + index: index, + is_from_route_finalized: is_from_route_finalized, }) } @@ -702,7 +746,7 @@ impl BlockChain { /// Supply a dummy parent total difficulty when the parent block may not be in the chain. /// Returns true if the block is disconnected. pub fn insert_unordered_block(&self, batch: &mut DBTransaction, bytes: &[u8], receipts: Vec, parent_td: Option, is_best: bool, is_ancient: bool) -> bool { - let block = BlockView::new(bytes); + let block = view!(BlockView, bytes); let header = block.header_view(); let hash = header.hash(); @@ -732,12 +776,11 @@ impl BlockChain { self.prepare_update(batch, ExtrasUpdate { block_hashes: self.prepare_block_hashes_update(bytes, &info), - block_details: self.prepare_block_details_update(bytes, &info), + block_details: self.prepare_block_details_update(bytes, &info, false, None), block_receipts: self.prepare_block_receipts_update(receipts, &info), blocks_blooms: self.prepare_block_blooms_update(bytes, &info), transactions_addresses: self.prepare_transaction_addresses_update(bytes, &info), info: info, - timestamp: header.timestamp(), block: bytes }, is_best); @@ -769,11 +812,14 @@ impl BlockChain { location: BlockLocation::CanonChain, }; + // TODO [sorpaas] support warp sync insertion of finalization and metadata. let block_details = BlockDetails { number: header.number(), total_difficulty: info.total_difficulty, parent: header.parent_hash(), children: Vec::new(), + is_finalized: false, + metadata: None, }; let mut update = HashMap::new(); @@ -786,7 +832,6 @@ impl BlockChain { blocks_blooms: self.prepare_block_blooms_update(bytes, &info), transactions_addresses: self.prepare_transaction_addresses_update(bytes, &info), info: info, - timestamp: header.timestamp(), block: bytes, }, is_best); true @@ -899,9 +944,24 @@ impl BlockChain { /// Inserts the block into backing cache database. /// Expects the block to be valid and already verified. /// If the block is already known, does nothing. - pub fn insert_block(&self, batch: &mut DBTransaction, bytes: &[u8], receipts: Vec) -> ImportRoute { + pub fn insert_block(&self, batch: &mut DBTransaction, bytes: &[u8], receipts: Vec, extras: ExtrasInsert) -> ImportRoute { + let block = view!(BlockView, bytes); + let header = block.header_view(); + + let parent_hash = header.parent_hash(); + let best_hash = self.best_block_hash(); + + let route = self.tree_route(best_hash, parent_hash).expect("forks are only kept when it has common ancestors; tree route from best to prospective's parent always exists; qed"); + + self.insert_block_with_route(batch, bytes, receipts, route, extras) + } + + /// Inserts the block into backing cache database with already generated route information. + /// Expects the block to be valid and already verified and route is tree route information from current best block to new block's parent. + /// If the block is already known, does nothing. + pub fn insert_block_with_route(&self, batch: &mut DBTransaction, bytes: &[u8], receipts: Vec, route: TreeRoute, extras: ExtrasInsert) -> ImportRoute { // create views onto rlp - let block = BlockView::new(bytes); + let block = view!(BlockView, bytes); let header = block.header_view(); let hash = header.hash(); @@ -918,7 +978,7 @@ impl BlockChain { batch.put(db::COL_HEADERS, &hash, &compressed_header); batch.put(db::COL_BODIES, &hash, &compressed_body); - let info = self.block_info(&header); + let info = self.block_info(&header, route, &extras); if let BlockLocation::BranchBecomingCanonChain(ref d) = info.location { info!(target: "reorg", "Reorg to {} ({} {} {})", @@ -931,12 +991,11 @@ impl BlockChain { self.prepare_update(batch, ExtrasUpdate { block_hashes: self.prepare_block_hashes_update(bytes, &info), - block_details: self.prepare_block_details_update(bytes, &info), + block_details: self.prepare_block_details_update(bytes, &info, extras.is_finalized, extras.metadata), block_receipts: self.prepare_block_receipts_update(receipts, &info), blocks_blooms: self.prepare_block_blooms_update(bytes, &info), transactions_addresses: self.prepare_transaction_addresses_update(bytes, &info), info: info.clone(), - timestamp: header.timestamp(), block: bytes, }, true); @@ -944,45 +1003,59 @@ impl BlockChain { } /// Get inserted block info which is critical to prepare extras updates. - fn block_info(&self, header: &HeaderView) -> BlockInfo { + fn block_info(&self, header: &HeaderView, route: TreeRoute, extras: &ExtrasInsert) -> BlockInfo { let hash = header.hash(); let number = header.number(); let parent_hash = header.parent_hash(); let parent_details = self.block_details(&parent_hash).unwrap_or_else(|| panic!("Invalid parent hash: {:?}", parent_hash)); - let is_new_best = parent_details.total_difficulty + header.difficulty() > self.best_block_total_difficulty(); BlockInfo { hash: hash, number: number, total_difficulty: parent_details.total_difficulty + header.difficulty(), - location: if is_new_best { - // on new best block we need to make sure that all ancestors - // are moved to "canon chain" - // find the route between old best block and the new one - let best_hash = self.best_block_hash(); - let route = self.tree_route(best_hash, parent_hash) - .expect("blocks being imported always within recent history; qed"); - - assert_eq!(number, parent_details.number + 1); - - match route.blocks.len() { - 0 => BlockLocation::CanonChain, - _ => { - let retracted = route.blocks.iter().take(route.index).cloned().collect::>().into_iter().collect::>(); - let enacted = route.blocks.into_iter().skip(route.index).collect::>(); - BlockLocation::BranchBecomingCanonChain(BranchBecomingCanonChainData { - ancestor: route.ancestor, - enacted: enacted, - retracted: retracted, - }) + location: match extras.fork_choice { + ForkChoice::New => { + // On new best block we need to make sure that all ancestors + // are moved to "canon chain" + // find the route between old best block and the new one + match route.blocks.len() { + 0 => BlockLocation::CanonChain, + _ => { + let retracted = route.blocks.iter().take(route.index).cloned().collect::>().into_iter().collect::>(); + let enacted = route.blocks.into_iter().skip(route.index).collect::>(); + BlockLocation::BranchBecomingCanonChain(BranchBecomingCanonChainData { + ancestor: route.ancestor, + enacted: enacted, + retracted: retracted, + }) + } } - } - } else { - BlockLocation::Branch - } + }, + ForkChoice::Old => BlockLocation::Branch, + }, } } + /// Mark a block to be considered finalized. Returns `Some(())` if the operation succeeds, and `None` if the block + /// hash is not found. + pub fn mark_finalized(&self, batch: &mut DBTransaction, block_hash: H256) -> Option<()> { + let mut block_details = self.block_details(&block_hash)?; + block_details.is_finalized = true; + + self.update_block_details(batch, block_hash, block_details); + Some(()) + } + + /// Prepares extras block detail update. + fn update_block_details(&self, batch: &mut DBTransaction, block_hash: H256, block_details: BlockDetails) { + let mut details_map = HashMap::new(); + details_map.insert(block_hash, block_details); + + // We're only updating one existing value. So it shouldn't suffer from cache decoherence problem. + let mut write_details = self.pending_block_details.write(); + batch.extend_with_cache(db::COL_EXTRA, &mut *write_details, details_map, CacheUpdatePolicy::Overwrite); + } + /// Prepares extras update. fn prepare_update(&self, batch: &mut DBTransaction, update: ExtrasUpdate, is_best: bool) { @@ -1028,12 +1101,11 @@ impl BlockChain { let mut best_block = self.pending_best_block.write(); if is_best && update.info.location != BlockLocation::Branch { batch.put(db::COL_EXTRA, b"best", &update.info.hash); + let block = encoded::Block::new(update.block.to_vec()); *best_block = Some(BestBlock { - hash: update.info.hash, - number: update.info.number, total_difficulty: update.info.total_difficulty, - timestamp: update.timestamp, - block: update.block.to_vec(), + header: block.decode_header(), + block, }); } @@ -1104,9 +1176,22 @@ impl BlockChain { } } + /// Iterator that lists `first` and then all of `first`'s ancestors, by extended header. + pub fn ancestry_with_metadata_iter<'a>(&'a self, first: H256) -> AncestryWithMetadataIter { + AncestryWithMetadataIter { + current: if self.is_known(&first) { + first + } else { + H256::default() // zero hash + }, + chain: self + } + } + /// Given a block's `parent`, find every block header which represents a valid possible uncle. - pub fn find_uncle_headers(&self, parent: &H256, uncle_generations: usize) -> Option> { - self.find_uncle_hashes(parent, uncle_generations).map(|v| v.into_iter().filter_map(|h| self.block_header(&h)).collect()) + pub fn find_uncle_headers(&self, parent: &H256, uncle_generations: usize) -> Option> { + self.find_uncle_hashes(parent, uncle_generations) + .map(|v| v.into_iter().filter_map(|h| self.block_header_data(&h)).collect()) } /// Given a block's `parent`, find every block hash which represents a valid possible uncle. @@ -1142,7 +1227,7 @@ impl BlockChain { /// This function returns modified block hashes. fn prepare_block_hashes_update(&self, block_bytes: &[u8], info: &BlockInfo) -> HashMap { let mut block_hashes = HashMap::new(); - let block = BlockView::new(block_bytes); + let block = view!(BlockView, block_bytes); let header = block.header_view(); let number = header.number(); @@ -1168,8 +1253,8 @@ impl BlockChain { /// This function returns modified block details. /// Uses the given parent details or attempts to load them from the database. - fn prepare_block_details_update(&self, block_bytes: &[u8], info: &BlockInfo) -> HashMap { - let block = BlockView::new(block_bytes); + fn prepare_block_details_update(&self, block_bytes: &[u8], info: &BlockInfo, is_finalized: bool, metadata: Option>) -> HashMap { + let block = view!(BlockView, block_bytes); let header = block.header_view(); let parent_hash = header.parent_hash(); @@ -1183,6 +1268,8 @@ impl BlockChain { total_difficulty: info.total_difficulty, parent: parent_hash, children: vec![], + is_finalized: is_finalized, + metadata: metadata, }; // write to batch @@ -1201,7 +1288,7 @@ impl BlockChain { /// This function returns modified transaction addresses. fn prepare_transaction_addresses_update(&self, block_bytes: &[u8], info: &BlockInfo) -> HashMap> { - let block = BlockView::new(block_bytes); + let block = view!(BlockView, block_bytes); let transaction_hashes = block.transaction_hashes(); match info.location { @@ -1269,7 +1356,7 @@ impl BlockChain { /// to bloom location in database (BlocksBloomLocation). /// fn prepare_block_blooms_update(&self, block_bytes: &[u8], info: &BlockInfo) -> HashMap { - let block = BlockView::new(block_bytes); + let block = view!(BlockView, block_bytes); let header = block.header_view(); let log_blooms = match info.location { @@ -1307,17 +1394,17 @@ impl BlockChain { /// Get best block hash. pub fn best_block_hash(&self) -> H256 { - self.best_block.read().hash + self.best_block.read().header.hash() } /// Get best block number. pub fn best_block_number(&self) -> BlockNumber { - self.best_block.read().number + self.best_block.read().header.number() } /// Get best block timestamp. pub fn best_block_timestamp(&self) -> u64 { - self.best_block.read().timestamp + self.best_block.read().header.timestamp() } /// Get best block total difficulty. @@ -1326,10 +1413,8 @@ impl BlockChain { } /// Get best block header - pub fn best_block_header(&self) -> encoded::Header { - let block = self.best_block.read(); - let raw = BlockView::new(&block.block).header_view().rlp().as_raw().to_vec(); - encoded::Header::new(raw) + pub fn best_block_header(&self) -> Header { + self.best_block.read().header.clone() } /// Get current cache size. @@ -1390,9 +1475,9 @@ impl BlockChain { /// Create a block body from a block. pub fn block_to_body(block: &[u8]) -> Bytes { let mut body = RlpStream::new_list(2); - let block_rlp = Rlp::new(block); - body.append_raw(block_rlp.at(1).as_raw(), 1); - body.append_raw(block_rlp.at(2).as_raw(), 1); + let block_view = view!(BlockView, block); + body.append_raw(block_view.transactions_rlp().as_raw(), 1); + body.append_raw(block_view.uncles_rlp().as_raw(), 1); body.out() } @@ -1402,12 +1487,12 @@ impl BlockChain { let best_block = self.best_block.read(); let best_ancient_block = self.best_ancient_block.read(); BlockChainInfo { - total_difficulty: best_block.total_difficulty.clone(), - pending_total_difficulty: best_block.total_difficulty.clone(), + total_difficulty: best_block.total_difficulty, + pending_total_difficulty: best_block.total_difficulty, genesis_hash: self.genesis_hash(), - best_block_hash: best_block.hash, - best_block_number: best_block.number, - best_block_timestamp: best_block.timestamp, + best_block_hash: best_block.header.hash(), + best_block_number: best_block.header.number(), + best_block_timestamp: best_block.header.timestamp(), first_block_hash: self.first_block(), first_block_number: From::from(self.first_block_number()), ancient_block_hash: best_ancient_block.as_ref().map(|b| b.hash), @@ -1422,12 +1507,12 @@ mod tests { use std::sync::Arc; use rustc_hex::FromHex; use hash::keccak; - use kvdb::KeyValueDB; + use kvdb::{KeyValueDB, DBTransaction}; use kvdb_memorydb; use ethereum_types::*; use receipt::{Receipt, TransactionOutcome}; use blockchain::{BlockProvider, BlockChain, Config, ImportRoute}; - use tests::helpers::{ + use test_helpers::{ generate_dummy_blockchain, generate_dummy_blockchain_with_extra, generate_dummy_empty_blockchain }; @@ -1445,6 +1530,42 @@ mod tests { BlockChain::new(Config::default(), genesis, db) } + fn insert_block(db: &Arc, bc: &BlockChain, bytes: &[u8], receipts: Vec) -> ImportRoute { + insert_block_commit(db, bc, bytes, receipts, true) + } + + fn insert_block_commit(db: &Arc, bc: &BlockChain, bytes: &[u8], receipts: Vec, commit: bool) -> ImportRoute { + let mut batch = db.transaction(); + let res = insert_block_batch(&mut batch, bc, bytes, receipts); + db.write(batch).unwrap(); + if commit { + bc.commit(); + } + res + } + + fn insert_block_batch(batch: &mut DBTransaction, bc: &BlockChain, bytes: &[u8], receipts: Vec) -> ImportRoute { + use views::BlockView; + use blockchain::ExtrasInsert; + + let block = view!(BlockView, bytes); + let header = block.header_view(); + let parent_hash = header.parent_hash(); + let parent_details = bc.block_details(&parent_hash).unwrap_or_else(|| panic!("Invalid parent hash: {:?}", parent_hash)); + let block_total_difficulty = parent_details.total_difficulty + header.difficulty(); + let fork_choice = if block_total_difficulty > bc.best_block_total_difficulty() { + ::engines::ForkChoice::New + } else { + ::engines::ForkChoice::Old + }; + + bc.insert_block(batch, bytes, receipts, ExtrasInsert { + fork_choice: fork_choice, + is_finalized: false, + metadata: None + }) + } + #[test] fn should_cache_best_block() { // given @@ -1456,8 +1577,7 @@ mod tests { assert_eq!(bc.best_block_number(), 0); // when - let mut batch = db.transaction(); - bc.insert_block(&mut batch, &first.last().encoded(), vec![]); + insert_block_commit(&db, &bc, &first.last().encoded(), vec![], false); assert_eq!(bc.best_block_number(), 0); bc.commit(); // NOTE no db.write here (we want to check if best block is cached) @@ -1487,7 +1607,7 @@ mod tests { assert_eq!(bc.block_details(&genesis_hash).unwrap().children, vec![]); let mut batch = db.transaction(); - bc.insert_block(&mut batch, &first.encoded(), vec![]); + insert_block_batch(&mut batch, &bc, &first.encoded(), vec![]); db.write(batch).unwrap(); bc.commit(); @@ -1513,7 +1633,7 @@ mod tests { let mut batch = db.transaction(); for block in generator { block_hashes.push(block.hash()); - bc.insert_block(&mut batch, &block.encoded(), vec![]); + insert_block_batch(&mut batch, &bc, &block.encoded(), vec![]); bc.commit(); } db.write(batch).unwrap(); @@ -1539,7 +1659,11 @@ mod tests { let b4b = b3a.add_block_with_difficulty(9); let b5b = b4a.add_block_with_difficulty(9); - let uncle_headers = vec![b4b.last().header(), b3b.last().header(), b2b.last().header()]; + let uncle_headers = vec![ + b4b.last().header().encoded(), + b3b.last().header().encoded(), + b2b.last().header().encoded(), + ]; let b4a_hash = b4a.last().hash(); let generator = BlockGenerator::new( @@ -1549,14 +1673,10 @@ mod tests { let db = new_db(); let bc = new_chain(&genesis.last().encoded(), db.clone()); - let mut batch = db.transaction(); for b in generator { - bc.insert_block(&mut batch, &b.encoded(), vec![]); - bc.commit(); + insert_block(&db, &bc, &b.encoded(), vec![]); } - db.write(batch).unwrap(); - assert_eq!(uncle_headers, bc.find_uncle_headers(&b4a_hash, 3).unwrap()); // TODO: insert block that already includes one of them as an uncle to check it's not allowed. } @@ -1590,9 +1710,9 @@ mod tests { let bc = new_chain(&genesis.last().encoded(), db.clone()); let mut batch = db.transaction(); - let _ = bc.insert_block(&mut batch, &b1a.last().encoded(), vec![]); + let _ = insert_block_batch(&mut batch, &bc, &b1a.last().encoded(), vec![]); bc.commit(); - let _ = bc.insert_block(&mut batch, &b1b.last().encoded(), vec![]); + let _ = insert_block_batch(&mut batch, &bc, &b1b.last().encoded(), vec![]); bc.commit(); db.write(batch).unwrap(); @@ -1604,7 +1724,7 @@ mod tests { // now let's make forked chain the canon chain let mut batch = db.transaction(); - let _ = bc.insert_block(&mut batch, &b2.last().encoded(), vec![]); + let _ = insert_block_batch(&mut batch, &bc, &b2.last().encoded(), vec![]); bc.commit(); db.write(batch).unwrap(); @@ -1665,9 +1785,9 @@ mod tests { let bc = new_chain(&genesis.last().encoded(), db.clone()); let mut batch = db.transaction(); - let _ = bc.insert_block(&mut batch, &b1a.last().encoded(), vec![]); + let _ = insert_block_batch(&mut batch, &bc, &b1a.last().encoded(), vec![]); bc.commit(); - let _ = bc.insert_block(&mut batch, &b1b.last().encoded(), vec![]); + let _ = insert_block_batch(&mut batch, &bc, &b1b.last().encoded(), vec![]); bc.commit(); db.write(batch).unwrap(); @@ -1683,7 +1803,7 @@ mod tests { // now let's make forked chain the canon chain let mut batch = db.transaction(); - let _ = bc.insert_block(&mut batch, &b2.last().encoded(), vec![]); + let _ = insert_block_batch(&mut batch, &bc, &b2.last().encoded(), vec![]); bc.commit(); db.write(batch).unwrap(); @@ -1723,16 +1843,16 @@ mod tests { let bc = new_chain(&genesis.last().encoded(), db.clone()); let mut batch = db.transaction(); - let ir1 = bc.insert_block(&mut batch, &b1.last().encoded(), vec![]); + let ir1 = insert_block_batch(&mut batch, &bc, &b1.last().encoded(), vec![]); bc.commit(); - let ir2 = bc.insert_block(&mut batch, &b2.last().encoded(), vec![]); + let ir2 = insert_block_batch(&mut batch, &bc, &b2.last().encoded(), vec![]); bc.commit(); - let ir3b = bc.insert_block(&mut batch, &b3b.last().encoded(), vec![]); + let ir3b = insert_block_batch(&mut batch, &bc, &b3b.last().encoded(), vec![]); bc.commit(); db.write(batch).unwrap(); assert_eq!(bc.block_hash(3).unwrap(), b3b_hash); let mut batch = db.transaction(); - let ir3a = bc.insert_block(&mut batch, &b3a.last().encoded(), vec![]); + let ir3a = insert_block_batch(&mut batch, &bc, &b3a.last().encoded(), vec![]); bc.commit(); db.write(batch).unwrap(); @@ -1837,7 +1957,7 @@ mod tests { let bc = new_chain(&genesis.last().encoded(), db.clone()); assert_eq!(bc.best_block_hash(), genesis_hash); let mut batch = db.transaction(); - bc.insert_block(&mut batch, &first.last().encoded(), vec![]); + insert_block_batch(&mut batch, &bc, &first.last().encoded(), vec![]); db.write(batch).unwrap(); bc.commit(); assert_eq!(bc.best_block_hash(), first_hash); @@ -1862,10 +1982,10 @@ mod tests { assert_eq!(bc.best_block_number(), 2999); let best_hash = bc.best_block_hash(); - let mut block_header = bc.block_header(&best_hash); + let mut block_header = bc.block_header_data(&best_hash); while !block_header.is_none() { - block_header = bc.block_header(block_header.unwrap().parent_hash()); + block_header = bc.block_header_data(&block_header.unwrap().parent_hash()); } assert!(bc.cache_size().blocks > 1024 * 1024); @@ -1896,7 +2016,7 @@ mod tests { let db = new_db(); let bc = new_chain(&genesis, db.clone()); let mut batch =db.transaction(); - bc.insert_block(&mut batch, &b1, vec![]); + insert_block_batch(&mut batch, &bc, &b1, vec![]); db.write(batch).unwrap(); bc.commit(); @@ -1907,14 +2027,6 @@ mod tests { } } - fn insert_block(db: &Arc, bc: &BlockChain, bytes: &[u8], receipts: Vec) -> ImportRoute { - let mut batch = db.transaction(); - let res = bc.insert_block(&mut batch, bytes, receipts); - db.write(batch).unwrap(); - bc.commit(); - res - } - #[test] fn test_logs() { let t1 = Transaction { @@ -1941,17 +2053,33 @@ mod tests { value: 103.into(), data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(), }.sign(&secret(), None); + let t4 = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Create, + value: 104.into(), + data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(), + }.sign(&secret(), None); let tx_hash1 = t1.hash(); let tx_hash2 = t2.hash(); let tx_hash3 = t3.hash(); + let tx_hash4 = t4.hash(); let genesis = BlockBuilder::genesis(); let b1 = genesis.add_block_with_transactions(vec![t1, t2]); let b2 = b1.add_block_with_transactions(iter::once(t3)); + let b3 = genesis.add_block_with(|| BlockOptions { + transactions: vec![t4.clone()], + difficulty: U256::from(9), + ..Default::default() + }); // Branch block let b1_hash = b1.last().hash(); let b1_number = b1.last().number(); let b2_hash = b2.last().hash(); let b2_number = b2.last().number(); + let b3_hash = b3.last().hash(); + let b3_number = b3.last().number(); let db = new_db(); let bc = new_chain(&genesis.last().encoded(), db.clone()); @@ -1982,10 +2110,21 @@ mod tests { ], } ]); + insert_block(&db, &bc, &b3.last().encoded(), vec![ + Receipt { + outcome: TransactionOutcome::StateRoot(H256::default()), + gas_used: 10_000.into(), + log_bloom: Default::default(), + logs: vec![ + LogEntry { address: Default::default(), topics: vec![], data: vec![5], }, + ], + } + ]); // when - let logs1 = bc.logs(vec![1, 2], |_| true, None); - let logs2 = bc.logs(vec![1, 2], |_| true, Some(1)); + let logs1 = bc.logs(vec![b1_hash, b2_hash], |_| true, None); + let logs2 = bc.logs(vec![b1_hash, b2_hash], |_| true, Some(1)); + let logs3 = bc.logs(vec![b3_hash], |_| true, None); // then assert_eq!(logs1, vec![ @@ -2037,6 +2176,17 @@ mod tests { log_index: 0, } ]); + assert_eq!(logs3, vec![ + LocalizedLogEntry { + entry: LogEntry { address: Default::default(), topics: vec![], data: vec![5] }, + block_hash: b3_hash, + block_number: b3_number, + transaction_hash: tx_hash4, + transaction_index: 0, + transaction_log_index: 0, + log_index: 0, + } + ]); } #[test] @@ -2160,12 +2310,12 @@ mod tests { let mut batch = db.transaction(); // create a longer fork for block in generator { - bc.insert_block(&mut batch, &block.encoded(), vec![]); + insert_block_batch(&mut batch, &bc, &block.encoded(), vec![]); bc.commit(); } assert_eq!(bc.best_block_number(), 5); - bc.insert_block(&mut batch, &uncle.last().encoded(), vec![]); + insert_block_batch(&mut batch, &bc, &uncle.last().encoded(), vec![]); db.write(batch).unwrap(); bc.commit(); } @@ -2192,7 +2342,7 @@ mod tests { // create a longer fork for (i, block) in generator.into_iter().enumerate() { - bc.insert_block(&mut batch, &block.encoded(), vec![]); + insert_block_batch(&mut batch, &bc, &block.encoded(), vec![]); bc.insert_epoch_transition(&mut batch, i as u64, EpochTransition { block_hash: block.hash(), block_number: i as u64 + 1, @@ -2203,7 +2353,7 @@ mod tests { assert_eq!(bc.best_block_number(), 5); - bc.insert_block(&mut batch, &uncle.last().encoded(), vec![]); + insert_block_batch(&mut batch, &bc, &uncle.last().encoded(), vec![]); bc.insert_epoch_transition(&mut batch, 999, EpochTransition { block_hash: uncle.last().hash(), block_number: 1, @@ -2253,11 +2403,7 @@ mod tests { // and a non-canonical fork of 8 from genesis. let fork_hash = { for block in fork_generator { - let mut batch = db.transaction(); - - bc.insert_block(&mut batch, &block.encoded(), vec![]); - bc.commit(); - db.write(batch).unwrap(); + insert_block(&db, &bc, &block.encoded(), vec![]); } assert_eq!(bc.best_block_number(), 7); @@ -2265,11 +2411,7 @@ mod tests { }; for block in next_generator { - let mut batch = db.transaction(); - bc.insert_block(&mut batch, &block.encoded(), vec![]); - bc.commit(); - - db.write(batch).unwrap(); + insert_block(&db, &bc, &block.encoded(), vec![]); } assert_eq!(bc.best_block_number(), 10); diff --git a/ethcore/src/blockchain/cache.rs b/ethcore/src/blockchain/cache.rs index 999be423df7945a13270e9378558c45c994e8308..0717011ae6cc35aebaff4ca69802e57cc1960c1f 100644 --- a/ethcore/src/blockchain/cache.rs +++ b/ethcore/src/blockchain/cache.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/src/blockchain/config.rs b/ethcore/src/blockchain/config.rs index 312289b060abdaf5b5ee738cd3a489b43283269a..632f978ac53c09f08544b12ba9914972fdc9a25e 100644 --- a/ethcore/src/blockchain/config.rs +++ b/ethcore/src/blockchain/config.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/src/blockchain/extras.rs b/ethcore/src/blockchain/extras.rs index 97583a693a6c9a72661ea00542577d30fbef1f6f..30dbec707e5d66c2d75f026a6ab92c32cd43a2f3 100644 --- a/ethcore/src/blockchain/extras.rs +++ b/ethcore/src/blockchain/extras.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -23,6 +23,7 @@ use db::Key; use engines::epoch::{Transition as EpochTransition}; use header::BlockNumber; use receipt::Receipt; +use rlp; use heapsize::HeapSizeOf; use ethereum_types::{H256, H264, U256}; @@ -167,7 +168,7 @@ impl Key for u64 { } /// Familial details concerning a block -#[derive(Debug, Clone, RlpEncodable, RlpDecodable)] +#[derive(Debug, Clone)] pub struct BlockDetails { /// Block number pub number: BlockNumber, @@ -177,6 +178,57 @@ pub struct BlockDetails { pub parent: H256, /// List of children block hashes pub children: Vec, + /// Whether the block is considered finalized + pub is_finalized: bool, + /// Additional block metadata + pub metadata: Option>, +} + +impl rlp::Encodable for BlockDetails { + fn rlp_append(&self, stream: &mut rlp::RlpStream) { + let use_short_version = self.metadata.is_none() && !self.is_finalized; + + match use_short_version { + true => { stream.begin_list(4); }, + false => { stream.begin_list(6); }, + } + + stream.append(&self.number); + stream.append(&self.total_difficulty); + stream.append(&self.parent); + stream.append_list(&self.children); + if !use_short_version { + stream.append(&self.is_finalized); + stream.append(&self.metadata); + } + } +} + +impl rlp::Decodable for BlockDetails { + fn decode(rlp: &rlp::Rlp) -> Result { + let use_short_version = match rlp.item_count()? { + 4 => true, + 6 => false, + _ => return Err(rlp::DecoderError::RlpIncorrectListLen), + }; + + Ok(BlockDetails { + number: rlp.val_at(0)?, + total_difficulty: rlp.val_at(1)?, + parent: rlp.val_at(2)?, + children: rlp.list_at(3)?, + is_finalized: if use_short_version { + false + } else { + rlp.val_at(4)? + }, + metadata: if use_short_version { + None + } else { + rlp.val_at(5)? + }, + }) + } } impl HeapSizeOf for BlockDetails { diff --git a/ethcore/src/blockchain/generator.rs b/ethcore/src/blockchain/generator.rs index 5f990a4d98050d83d618f6bcdf96f584c0c446b7..5a97f37f988d44e8aeaf428a0a99a831f65e5e35 100644 --- a/ethcore/src/blockchain/generator.rs +++ b/ethcore/src/blockchain/generator.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -41,7 +41,7 @@ impl Block { #[inline] pub fn hash(&self) -> H256 { - BlockView::new(&self.encoded()).header_view().hash() + view!(BlockView, &self.encoded()).header_view().hash() } #[inline] diff --git a/ethcore/src/blockchain/import_route.rs b/ethcore/src/blockchain/import_route.rs index cf5d3ca1e78d2542877d97e13c8408ba9cedbc69..d8b38e6335ed7a728916cfd50f0a12f58865e5f0 100644 --- a/ethcore/src/blockchain/import_route.rs +++ b/ethcore/src/blockchain/import_route.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ use ethereum_types::H256; use blockchain::block_info::{BlockInfo, BlockLocation}; /// Import route for newly inserted block. -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct ImportRoute { /// Blocks that were invalidated by new block. pub retracted: Vec, diff --git a/ethcore/src/blockchain/mod.rs b/ethcore/src/blockchain/mod.rs index 062068c704de03d2b8d049dead0525fba3d292f1..6389f308aafaec24fc31cc120e840a364185ef64 100644 --- a/ethcore/src/blockchain/mod.rs +++ b/ethcore/src/blockchain/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -33,4 +33,5 @@ pub use self::cache::CacheSize; pub use self::config::Config; pub use self::extras::{BlockReceipts, BlockDetails, TransactionAddress}; pub use self::import_route::ImportRoute; +pub use self::update::ExtrasInsert; pub use types::tree_route::TreeRoute; diff --git a/ethcore/src/blockchain/update.rs b/ethcore/src/blockchain/update.rs index 467ccc8d17b7a93dc0162aab350c3ead20bd0798..8960d795aa73ec002f9641008565ab95e77c1072 100644 --- a/ethcore/src/blockchain/update.rs +++ b/ethcore/src/blockchain/update.rs @@ -1,3 +1,19 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + use std::collections::HashMap; use ethereum_types::H256; use header::BlockNumber; @@ -9,8 +25,6 @@ use blooms::{BloomGroup, GroupPosition}; pub struct ExtrasUpdate<'a> { /// Block info. pub info: BlockInfo, - /// Block timestamp. - pub timestamp: u64, /// Current block uncompressed rlp bytes pub block: &'a [u8], /// Modified block hashes. @@ -24,3 +38,13 @@ pub struct ExtrasUpdate<'a> { /// Modified transaction addresses (None signifies removed transactions). pub transactions_addresses: HashMap>, } + +/// Extra information in block insertion. +pub struct ExtrasInsert { + /// The primitive fork choice before applying finalization rules. + pub fork_choice: ::engines::ForkChoice, + /// Is the inserted block considered finalized. + pub is_finalized: bool, + /// New block local metadata. + pub metadata: Option>, +} diff --git a/ethcore/src/blooms/bloom_group.rs b/ethcore/src/blooms/bloom_group.rs index 4b47b1ad9473b9ff55068d34d4720c7e854e668d..0eb1e9c5297604e3eb01915d1ace615d2ccfa4ea 100644 --- a/ethcore/src/blooms/bloom_group.rs +++ b/ethcore/src/blooms/bloom_group.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/src/blooms/group_position.rs b/ethcore/src/blooms/group_position.rs index b1ea82792618d500052285929ad1fde41f12d647..1f9ddca7e07d39f920d78c660a61e4073f8c2f49 100644 --- a/ethcore/src/blooms/group_position.rs +++ b/ethcore/src/blooms/group_position.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/src/blooms/mod.rs b/ethcore/src/blooms/mod.rs index a66485782b31f3373062ad636f307a737395ed30..7658446c745ad76326f1612a53203d9fc7525e33 100644 --- a/ethcore/src/blooms/mod.rs +++ b/ethcore/src/blooms/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/src/builtin.rs b/ethcore/src/builtin.rs index c18e1f3cd304a0ca34a9c928c6853f571e094497..61739c7b1d4a163e11b1d25b37493a1d03ed8e3f 100644 --- a/ethcore/src/builtin.rs +++ b/ethcore/src/builtin.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -18,9 +18,7 @@ use std::cmp::{max, min}; use std::io::{self, Read}; use byteorder::{ByteOrder, BigEndian}; -use crypto::sha2::Sha256 as Sha256Digest; -use crypto::ripemd160::Ripemd160 as Ripemd160Digest; -use crypto::digest::Digest; +use ethcore_crypto::digest; use num::{BigUint, Zero, One}; use hash::keccak; @@ -295,28 +293,17 @@ impl Impl for EcRecover { impl Impl for Sha256 { fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error> { - let mut sha = Sha256Digest::new(); - sha.input(input); - - let mut out = [0; 32]; - sha.result(&mut out); - - output.write(0, &out); - + let d = digest::sha256(input); + output.write(0, &*d); Ok(()) } } impl Impl for Ripemd160 { fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error> { - let mut sha = Ripemd160Digest::new(); - sha.input(input); - - let mut out = [0; 32]; - sha.result(&mut out[12..32]); - - output.write(0, &out); - + let hash = digest::ripemd160(input); + output.write(0, &[0; 12][..]); + output.write(12, &hash); Ok(()) } } @@ -716,7 +703,6 @@ mod tests { assert_eq!(f.cost(&input[..]), expected_cost.into()); } - // test for potential exp len overflow { let input = FromHex::from_hex("\ @@ -840,7 +826,6 @@ mod tests { assert_eq!(output, expected); } - // no input, should not fail { let mut empty = [0u8; 0]; @@ -872,7 +857,6 @@ mod tests { } } - #[test] fn bn128_mul() { diff --git a/ethcore/src/cache_manager.rs b/ethcore/src/cache_manager.rs index 7d91dcc0d005c6a2b3ed5d445940f6e35bb40a65..4199cb1d59323161196ff954074065d941b3b5ed 100644 --- a/ethcore/src/cache_manager.rs +++ b/ethcore/src/cache_manager.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/src/client/ancient_import.rs b/ethcore/src/client/ancient_import.rs index 13699ea5a0e5d2cc0f9c578c55c4e68aee8780e6..4586a04eed2b4849f824d10616de13827050b34b 100644 --- a/ethcore/src/client/ancient_import.rs +++ b/ethcore/src/client/ancient_import.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -32,16 +32,16 @@ const HEAVY_VERIFY_RATE: f32 = 0.02; /// Ancient block verifier: import an ancient sequence of blocks in order from a starting /// epoch. pub struct AncientVerifier { - cur_verifier: RwLock>>, + cur_verifier: RwLock>>>, engine: Arc, } impl AncientVerifier { - /// Create a new ancient block verifier with the given engine and initial verifier. - pub fn new(engine: Arc, start_verifier: Box>) -> Self { + /// Create a new ancient block verifier with the given engine. + pub fn new(engine: Arc) -> Self { AncientVerifier { - cur_verifier: RwLock::new(start_verifier), - engine: engine, + cur_verifier: RwLock::new(None), + engine, } } @@ -53,17 +53,49 @@ impl AncientVerifier { header: &Header, chain: &BlockChain, ) -> Result<(), ::error::Error> { - match rng.gen::() <= HEAVY_VERIFY_RATE { - true => self.cur_verifier.read().verify_heavy(header)?, - false => self.cur_verifier.read().verify_light(header)?, + // perform verification + let verified = if let Some(ref cur_verifier) = *self.cur_verifier.read() { + match rng.gen::() <= HEAVY_VERIFY_RATE { + true => cur_verifier.verify_heavy(header)?, + false => cur_verifier.verify_light(header)?, + } + true + } else { + false + }; + + // when there is no verifier initialize it. + // We use a bool flag to avoid double locking in the happy case + if !verified { + { + let mut cur_verifier = self.cur_verifier.write(); + if cur_verifier.is_none() { + *cur_verifier = Some(self.initial_verifier(header, chain)?); + } + } + // Call again to verify. + return self.verify(rng, header, chain); } // ancient import will only use transitions obtained from the snapshot. if let Some(transition) = chain.epoch_transition(header.number(), header.hash()) { let v = self.engine.epoch_verifier(&header, &transition.proof).known_confirmed()?; - *self.cur_verifier.write() = v; + *self.cur_verifier.write() = Some(v); } Ok(()) } + + fn initial_verifier(&self, header: &Header, chain: &BlockChain) + -> Result>, ::error::Error> + { + trace!(target: "client", "Initializing ancient block restoration."); + let current_epoch_data = chain.epoch_transitions() + .take_while(|&(_, ref t)| t.block_number < header.number()) + .last() + .map(|(_, t)| t.proof) + .expect("At least one epoch entry (genesis) always stored; qed"); + + self.engine.epoch_verifier(&header, ¤t_epoch_data).known_confirmed() + } } diff --git a/ethcore/src/client/chain_notify.rs b/ethcore/src/client/chain_notify.rs index d05c95b8c850cfc03b829de3950da602c8278982..62de03591dfb88c9b53d5ffaeaf3b8dc265bdf04 100644 --- a/ethcore/src/client/chain_notify.rs +++ b/ethcore/src/client/chain_notify.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -14,8 +14,105 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use ethereum_types::H256; use bytes::Bytes; +use ethereum_types::H256; +use transaction::UnverifiedTransaction; +use blockchain::ImportRoute; +use std::time::Duration; +use std::collections::HashMap; + +/// Messages to broadcast via chain +pub enum ChainMessageType { + /// Consensus message + Consensus(Vec), + /// Message with private transaction + PrivateTransaction(Vec), + /// Message with signed private transaction + SignedPrivateTransaction(Vec), +} + +/// Route type to indicate whether it is enacted or retracted. +#[derive(Clone)] +pub enum ChainRouteType { + /// Enacted block + Enacted, + /// Retracted block + Retracted +} + +/// A complete chain enacted retracted route. +#[derive(Default, Clone)] +pub struct ChainRoute { + route: Vec<(H256, ChainRouteType)>, + enacted: Vec, + retracted: Vec, +} + +impl<'a> From<&'a [ImportRoute]> for ChainRoute { + fn from(import_results: &'a [ImportRoute]) -> ChainRoute { + ChainRoute::new(import_results.iter().flat_map(|route| { + route.retracted.iter().map(|h| (*h, ChainRouteType::Retracted)) + .chain(route.enacted.iter().map(|h| (*h, ChainRouteType::Enacted))) + }).collect()) + } +} + +impl ChainRoute { + /// Create a new ChainRoute based on block hash and route type pairs. + pub fn new(route: Vec<(H256, ChainRouteType)>) -> Self { + let (enacted, retracted) = Self::to_enacted_retracted(&route); + + Self { route, enacted, retracted } + } + + /// Gather all non-duplicate enacted and retracted blocks. + fn to_enacted_retracted(route: &[(H256, ChainRouteType)]) -> (Vec, Vec) { + fn map_to_vec(map: Vec<(H256, bool)>) -> Vec { + map.into_iter().map(|(k, _v)| k).collect() + } + + // Because we are doing multiple inserts some of the blocks that were enacted in import `k` + // could be retracted in import `k+1`. This is why to understand if after all inserts + // the block is enacted or retracted we iterate over all routes and at the end final state + // will be in the hashmap + let map = route.iter().fold(HashMap::new(), |mut map, route| { + match &route.1 { + &ChainRouteType::Enacted => { + map.insert(route.0, true); + }, + &ChainRouteType::Retracted => { + map.insert(route.0, false); + }, + } + map + }); + + // Split to enacted retracted (using hashmap value) + let (enacted, retracted) = map.into_iter().partition(|&(_k, v)| v); + // And convert tuples to keys + (map_to_vec(enacted), map_to_vec(retracted)) + } + + /// Consume route and return the enacted retracted form. + pub fn into_enacted_retracted(self) -> (Vec, Vec) { + (self.enacted, self.retracted) + } + + /// All non-duplicate enacted blocks. + pub fn enacted(&self) -> &[H256] { + &self.enacted + } + + /// All non-duplicate retracted blocks. + pub fn retracted(&self) -> &[H256] { + &self.retracted + } + + /// All blocks in the route. + pub fn route(&self) -> &[(H256, ChainRouteType)] { + &self.route + } +} /// Represents what has to be handled by actor listening to chain events pub trait ChainNotify : Send + Sync { @@ -24,12 +121,11 @@ pub trait ChainNotify : Send + Sync { &self, _imported: Vec, _invalid: Vec, - _enacted: Vec, - _retracted: Vec, + _route: ChainRoute, _sealed: Vec, // Block bytes. _proposed: Vec, - _duration: u64, + _duration: Duration, ) { // does nothing by default } @@ -45,11 +141,11 @@ pub trait ChainNotify : Send + Sync { } /// fires when chain broadcasts a message - fn broadcast(&self, _data: Vec) {} + fn broadcast(&self, _message_type: ChainMessageType) {} /// fires when new transactions are received from a peer fn transactions_received(&self, - _hashes: Vec, + _txs: &[UnverifiedTransaction], _peer_id: usize, ) { // does nothing by default diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index c4c2b6a2ee63f1ea2b8c6533ccbd7c1f6534b113..f1e5d882ead78abf2437b8a0551524dff5e1de3e 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -14,16 +14,17 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use std::collections::{HashSet, HashMap, BTreeMap, VecDeque}; +use std::collections::{HashSet, BTreeMap, BTreeSet, VecDeque}; +use std::fmt; use std::str::FromStr; -use std::sync::{Arc, Weak}; use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering as AtomicOrdering}; -use std::time::{Instant}; -use itertools::Itertools; +use std::sync::{Arc, Weak}; +use std::time::{Instant, Duration}; // util use hash::keccak; use bytes::Bytes; +use itertools::Itertools; use journaldb; use trie::{TrieSpec, TrieFactory, Trie}; use kvdb::{DBValue, KeyValueDB, DBTransaction}; @@ -32,7 +33,7 @@ use util_error::UtilError; // other use ethereum_types::{H256, Address, U256}; use block::{IsBlock, LockedBlock, Drain, ClosedBlock, OpenBlock, enact_verified, SealedBlock}; -use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute, TransactionAddress}; +use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute, TransactionAddress, ExtrasInsert}; use client::ancient_import::AncientVerifier; use client::Error as ClientError; use client::{ @@ -44,37 +45,39 @@ use client::{ }; use client::{ BlockId, TransactionId, UncleId, TraceId, ClientConfig, BlockChainClient, - MiningBlockChainClient, TraceFilter, CallAnalytics, BlockImportError, Mode, - ChainNotify, PruningInfo, ProvingBlockChainClient, EngineInfo + TraceFilter, CallAnalytics, BlockImportError, Mode, + ChainNotify, ChainRoute, PruningInfo, ProvingBlockChainClient, EngineInfo, ChainMessageType, + IoClient, }; use encoded; -use engines::{EthEngine, EpochTransition}; -use error::{ImportError, ExecutionError, CallError, BlockError, ImportResult, Error as EthcoreError}; +use engines::{EthEngine, EpochTransition, ForkChoice}; +use error::{ImportErrorKind, BlockImportErrorKind, ExecutionError, CallError, BlockError, ImportResult, Error as EthcoreError}; use vm::{EnvInfo, LastHashes}; use evm::Schedule; use executive::{Executive, Executed, TransactOptions, contract_address}; use factory::{Factories, VmFactory}; -use header::{BlockNumber, Header}; -use io::IoChannel; +use header::{BlockNumber, Header, ExtendedHeader}; +use io::{IoChannel, IoError}; use log_entry::LocalizedLogEntry; use miner::{Miner, MinerService}; +use ethcore_miner::pool::VerifiedTransaction; use parking_lot::{Mutex, RwLock}; use rand::OsRng; use receipt::{Receipt, LocalizedReceipt}; -use rlp::UntrustedRlp; use snapshot::{self, io as snapshot_io}; use spec::Spec; use state_db::StateDB; use state::{self, State}; use trace; use trace::{TraceDB, ImportRequest as TraceImportRequest, LocalizedTrace, Database as TraceDatabase}; -use transaction::{self, LocalizedTransaction, UnverifiedTransaction, SignedTransaction, Transaction, PendingTransaction, Action}; +use transaction::{self, LocalizedTransaction, UnverifiedTransaction, SignedTransaction, Transaction, Action}; use types::filter::Filter; -use types::mode::Mode as IpcMode; +use types::ancestry_action::AncestryAction; use verification; use verification::{PreverifiedBlock, Verifier}; use verification::queue::BlockQueue; use views::BlockView; +use parity_machine::{Finalizable, WithMetadata}; // re-export pub use types::blockchain_info::BlockChainInfo; @@ -85,6 +88,9 @@ pub use verification::queue::QueueInfo as BlockQueueInfo; use_contract!(registry, "Registry", "res/contracts/registrar.json"); const MAX_TX_QUEUE_SIZE: usize = 4096; +const MAX_ANCIENT_BLOCKS_QUEUE_SIZE: usize = 4096; +// Max number of blocks imported at once. +const MAX_ANCIENT_BLOCKS_TO_IMPORT: usize = 4; const MAX_QUEUE_SIZE_TO_SLEEP_ON: usize = 2; const MIN_HISTORY_SIZE: u64 = 8; @@ -103,10 +109,10 @@ pub struct ClientReport { impl ClientReport { /// Alter internal reporting to reflect the additional `block` has been processed. - pub fn accrue_block(&mut self, block: &PreverifiedBlock) { + pub fn accrue_block(&mut self, header: &Header, transactions: usize) { self.blocks_imported += 1; - self.transactions_applied += block.transactions.len(); - self.gas_processed = self.gas_processed + block.header.gas_used().clone(); + self.transactions_applied += transactions; + self.gas_processed = self.gas_processed + *header.gas_used(); } } @@ -120,7 +126,7 @@ impl<'a> ::std::ops::Sub<&'a ClientReport> for ClientReport { self.blocks_imported -= other.blocks_imported; self.transactions_applied -= other.transactions_applied; self.gas_processed = self.gas_processed - other.gas_processed; - self.state_db_mem = higher_mem - lower_mem; + self.state_db_mem = higher_mem - lower_mem; self } @@ -154,10 +160,7 @@ struct Importer { pub miner: Arc, /// Ancient block verifier: import an ancient sequence of blocks in order from a starting epoch - pub ancient_verifier: Mutex>, - - /// Random number generator used by `AncientVerifier` - pub rng: Mutex, + pub ancient_verifier: AncientVerifier, /// Ethereum engine to be used during import pub engine: Arc, @@ -204,8 +207,19 @@ pub struct Client { /// List of actors to be notified on certain chain events notify: RwLock>>, - /// Count of pending transactions in the queue - queue_transactions: AtomicUsize, + /// Queued transactions from IO + queue_transactions: IoChannelQueue, + /// Ancient blocks import queue + queue_ancient_blocks: IoChannelQueue, + /// Queued ancient blocks, make sure they are imported in order. + queued_ancient_blocks: Arc, + VecDeque<(Header, Bytes, Bytes)> + )>>, + ancient_blocks_import_lock: Arc>, + /// Consensus messages import queue + queue_consensus_message: IoChannelQueue, + last_hashes: RwLock>, factories: Factories, @@ -220,7 +234,7 @@ pub struct Client { registrar_address: Option
, /// A closure to call when we want to restart the client - exit_handler: Mutex) + 'static + Send>>>, + exit_handler: Mutex>>, importer: Importer, } @@ -239,38 +253,11 @@ impl Importer { verifier: verification::new(config.verifier_type.clone()), block_queue, miner, - ancient_verifier: Mutex::new(None), - rng: Mutex::new(OsRng::new()?), + ancient_verifier: AncientVerifier::new(engine.clone()), engine, }) } - fn calculate_enacted_retracted(&self, import_results: &[ImportRoute]) -> (Vec, Vec) { - fn map_to_vec(map: Vec<(H256, bool)>) -> Vec { - map.into_iter().map(|(k, _v)| k).collect() - } - - // In ImportRoute we get all the blocks that have been enacted and retracted by single insert. - // Because we are doing multiple inserts some of the blocks that were enacted in import `k` - // could be retracted in import `k+1`. This is why to understand if after all inserts - // the block is enacted or retracted we iterate over all routes and at the end final state - // will be in the hashmap - let map = import_results.iter().fold(HashMap::new(), |mut map, route| { - for hash in &route.enacted { - map.insert(hash.clone(), true); - } - for hash in &route.retracted { - map.insert(hash.clone(), false); - } - map - }); - - // Split to enacted retracted (using hashmap value) - let (enacted, retracted) = map.into_iter().partition(|&(_k, v)| v); - // And convert tuples to keys - (map_to_vec(enacted), map_to_vec(retracted)) - } - /// This is triggered by a message coming from a block queue when the block is ready for insertion pub fn import_verified_blocks(&self, client: &Client) -> usize { @@ -295,23 +282,29 @@ impl Importer { let start = Instant::now(); for block in blocks { - let header = &block.header; + let header = block.header.clone(); + let bytes = block.bytes.clone(); + let hash = header.hash(); + let is_invalid = invalid_blocks.contains(header.parent_hash()); if is_invalid { - invalid_blocks.insert(header.hash()); + invalid_blocks.insert(hash); continue; } - if let Ok(closed_block) = self.check_and_close_block(&block, client) { - if self.engine.is_proposal(&block.header) { - self.block_queue.mark_as_good(&[header.hash()]); - proposed_blocks.push(block.bytes); + + if let Ok(closed_block) = self.check_and_close_block(block, client) { + if self.engine.is_proposal(&header) { + self.block_queue.mark_as_good(&[hash]); + proposed_blocks.push(bytes); } else { - imported_blocks.push(header.hash()); + imported_blocks.push(hash); + + let transactions_len = closed_block.transactions().len(); - let route = self.commit_block(closed_block, &header, &block.bytes, client); + let route = self.commit_block(closed_block, &header, &bytes, client); import_results.push(route); - client.report.write().accrue_block(&block); + client.report.write().accrue_block(&header, transactions_len); } } else { invalid_blocks.insert(header.hash()); @@ -325,27 +318,22 @@ impl Importer { self.block_queue.mark_as_bad(&invalid_blocks); } let is_empty = self.block_queue.mark_as_good(&imported_blocks); - let duration_ns = { - let elapsed = start.elapsed(); - elapsed.as_secs() * 1_000_000_000 + elapsed.subsec_nanos() as u64 - }; - (imported_blocks, import_results, invalid_blocks, imported, proposed_blocks, duration_ns, is_empty) + (imported_blocks, import_results, invalid_blocks, imported, proposed_blocks, start.elapsed(), is_empty) }; { if !imported_blocks.is_empty() && is_empty { - let (enacted, retracted) = self.calculate_enacted_retracted(&import_results); + let route = ChainRoute::from(import_results.as_ref()); if is_empty { - self.miner.chain_new_blocks(client, &imported_blocks, &invalid_blocks, &enacted, &retracted); + self.miner.chain_new_blocks(client, &imported_blocks, &invalid_blocks, route.enacted(), route.retracted(), false); } client.notify(|notify| { notify.new_blocks( imported_blocks.clone(), invalid_blocks.clone(), - enacted.clone(), - retracted.clone(), + route.clone(), Vec::new(), proposed_blocks.clone(), duration, @@ -358,20 +346,19 @@ impl Importer { imported } - fn check_and_close_block(&self, block: &PreverifiedBlock, client: &Client) -> Result { + fn check_and_close_block(&self, block: PreverifiedBlock, client: &Client) -> Result { let engine = &*self.engine; - let header = &block.header; + let header = block.header.clone(); - let chain = client.chain.read(); // Check the block isn't so old we won't be able to enact it. - let best_block_number = chain.best_block_number(); + let best_block_number = client.chain.read().best_block_number(); if client.pruning_info().earliest_state > header.number() { warn!(target: "client", "Block import failed for #{} ({})\nBlock is ancient (current best block: #{}).", header.number(), header.hash(), best_block_number); return Err(()); } // Check if parent is in chain - let parent = match chain.block_header(header.parent_hash()) { + let parent = match client.block_header_decoded(BlockId::Hash(*header.parent_hash())) { Some(h) => h, None => { warn!(target: "client", "Block import failed for #{} ({}): Parent not found ({}) ", header.number(), header.hash(), header.parent_hash()); @@ -379,9 +366,10 @@ impl Importer { } }; + let chain = client.chain.read(); // Verify Block Family let verify_family_result = self.verifier.verify_block_family( - header, + &header, &parent, engine, Some(verification::FullFamilyParams { @@ -397,7 +385,7 @@ impl Importer { return Err(()); }; - let verify_external_result = self.verifier.verify_block_external(header, engine); + let verify_external_result = self.verifier.verify_block_external(&header, engine); if let Err(e) = verify_external_result { warn!(target: "client", "Stage 4 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); return Err(()); @@ -408,7 +396,8 @@ impl Importer { let db = client.state_db.read().boxed_clone_canon(header.parent_hash()); let is_epoch_begin = chain.epoch_transition(parent.number(), *header.parent_hash()).is_some(); - let enact_result = enact_verified(block, + let enact_result = enact_verified( + block, engine, client.tracedb.read().tracing_enabled(), db, @@ -416,17 +405,24 @@ impl Importer { last_hashes, client.factories.clone(), is_epoch_begin, + &mut chain.ancestry_with_metadata_iter(*header.parent_hash()), ); + let mut locked_block = enact_result.map_err(|e| { warn!(target: "client", "Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); })?; - if header.number() < engine.params().validate_receipts_transition && header.receipts_root() != locked_block.block().header().receipts_root() { - locked_block = locked_block.strip_receipts(); + // Strip receipts for blocks before validate_receipts_transition, + // if the expected receipts root header does not match. + // (i.e. allow inconsistency in receipts outcome before the transition block) + if header.number() < engine.params().validate_receipts_transition + && header.receipts_root() != locked_block.block().header().receipts_root() + { + locked_block.strip_receipts_outcomes(); } // Final Verification - if let Err(e) = self.verifier.verify_block_final(header, locked_block.block().header()) { + if let Err(e) = self.verifier.verify_block_final(&header, locked_block.block().header()) { warn!(target: "client", "Stage 5 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); return Err(()); } @@ -438,53 +434,20 @@ impl Importer { /// /// The block is guaranteed to be the next best blocks in the /// first block sequence. Does no sealing or transaction validation. - fn import_old_block(&self, block_bytes: Bytes, receipts_bytes: Bytes, db: &KeyValueDB, chain: &BlockChain) -> Result { - let block = BlockView::new(&block_bytes); - let header = block.header(); - let receipts = ::rlp::decode_list(&receipts_bytes); + fn import_old_block(&self, header: &Header, block_bytes: &[u8], receipts_bytes: &[u8], db: &KeyValueDB, chain: &BlockChain) -> Result { + let receipts = ::rlp::decode_list(receipts_bytes); let hash = header.hash(); let _import_lock = self.import_lock.lock(); { trace_time!("import_old_block"); - let mut ancient_verifier = self.ancient_verifier.lock(); - - { - // closure for verifying a block. - let verify_with = |verifier: &AncientVerifier| -> Result<(), ::error::Error> { - // verify the block, passing the chain for updating the epoch - // verifier. - let mut rng = OsRng::new().map_err(UtilError::from)?; - verifier.verify(&mut rng, &header, &chain) - }; - - // initialize the ancient block verifier if we don't have one already. - match &mut *ancient_verifier { - &mut Some(ref verifier) => { - verify_with(verifier)? - } - x @ &mut None => { - // load most recent epoch. - trace!(target: "client", "Initializing ancient block restoration."); - let current_epoch_data = chain.epoch_transitions() - .take_while(|&(_, ref t)| t.block_number < header.number()) - .last() - .map(|(_, t)| t.proof) - .expect("At least one epoch entry (genesis) always stored; qed"); - - let current_verifier = self.engine.epoch_verifier(&header, ¤t_epoch_data) - .known_confirmed()?; - let current_verifier = AncientVerifier::new(self.engine.clone(), current_verifier); - - verify_with(¤t_verifier)?; - *x = Some(current_verifier); - } - } - } + // verify the block, passing the chain for updating the epoch verifier. + let mut rng = OsRng::new().map_err(UtilError::from)?; + self.ancient_verifier.verify(&mut rng, &header, &chain)?; // Commit results let mut batch = DBTransaction::new(); - chain.insert_unordered_block(&mut batch, &block_bytes, receipts, None, false, true); + chain.insert_unordered_block(&mut batch, block_bytes, receipts, None, false, true); // Final commit to the DB db.write_buffered(batch); chain.commit(); @@ -507,12 +470,50 @@ impl Importer { let receipts = block.receipts().to_owned(); let traces = block.traces().clone().drain(); - assert_eq!(header.hash(), BlockView::new(block_data).header_view().hash()); + assert_eq!(header.hash(), view!(BlockView, block_data).header_view().hash()); //let traces = From::from(block.traces().clone().unwrap_or_else(Vec::new)); let mut batch = DBTransaction::new(); + let ancestry_actions = self.engine.ancestry_actions(block.block(), &mut chain.ancestry_with_metadata_iter(*parent)); + + let best_hash = chain.best_block_hash(); + let metadata = block.block().metadata().map(Into::into); + let is_finalized = block.block().is_finalized(); + + let new = ExtendedHeader { + header: header.clone(), + is_finalized: is_finalized, + metadata: metadata, + parent_total_difficulty: chain.block_details(&parent).expect("Parent block is in the database; qed").total_difficulty + }; + + let best = { + let hash = best_hash; + let header = chain.block_header_data(&hash) + .expect("Best block is in the database; qed") + .decode() + .expect("Stored block header is valid RLP; qed"); + let details = chain.block_details(&hash) + .expect("Best block is in the database; qed"); + + ExtendedHeader { + parent_total_difficulty: details.total_difficulty - *header.difficulty(), + is_finalized: details.is_finalized, + metadata: details.metadata, + + header: header, + } + }; + + let route = chain.tree_route(best_hash, *parent).expect("forks are only kept when it has common ancestors; tree route from best to prospective's parent always exists; qed"); + let fork_choice = if route.is_from_route_finalized { + ForkChoice::Old + } else { + self.engine.fork_choice(&new, &best) + }; + // CHECK! I *think* this is fine, even if the state_root is equal to another // already-imported block of the same number. // TODO: Prove it with a test. @@ -531,7 +532,17 @@ impl Importer { ); state.journal_under(&mut batch, number, hash).expect("DB commit failed"); - let route = chain.insert_block(&mut batch, block_data, receipts.clone()); + + for ancestry_action in ancestry_actions { + let AncestryAction::MarkFinalized(ancestry) = ancestry_action; + chain.mark_finalized(&mut batch, ancestry).expect("Engine's ancestry action must be known blocks; qed"); + } + + let route = chain.insert_block(&mut batch, block_data, receipts.clone(), ExtrasInsert { + fork_choice: fork_choice, + is_finalized: is_finalized, + metadata: new.metadata, + }); client.tracedb.read().import(&mut batch, TraceImportRequest { traces: traces.into(), @@ -655,7 +666,7 @@ impl Importer { fn check_epoch_end<'a>(&self, header: &'a Header, chain: &BlockChain, client: &Client) { let is_epoch_end = self.engine.is_epoch_end( header, - &(|hash| chain.block_header(&hash)), + &(|hash| client.block_header_decoded(BlockId::Hash(hash))), &(|hash| chain.get_pending_transition(hash)), // TODO: limit to current epoch. ); @@ -724,7 +735,7 @@ impl Client { config.history }; - if !chain.block_header(&chain.best_block_hash()).map_or(true, |h| state_db.journal_db().contains(h.state_root())) { + if !chain.block_header_data(&chain.best_block_hash()).map_or(true, |h| state_db.journal_db().contains(&h.state_root())) { warn!("State root not found for block #{} ({:x})", chain.best_block_number(), chain.best_block_hash()); } @@ -754,7 +765,11 @@ impl Client { report: RwLock::new(Default::default()), io_channel: Mutex::new(message_channel), notify: RwLock::new(Vec::new()), - queue_transactions: AtomicUsize::new(0), + queue_transactions: IoChannelQueue::new(MAX_TX_QUEUE_SIZE), + queue_ancient_blocks: IoChannelQueue::new(MAX_ANCIENT_BLOCKS_QUEUE_SIZE), + queued_ancient_blocks: Default::default(), + ancient_blocks_import_lock: Default::default(), + queue_consensus_message: IoChannelQueue::new(usize::max_value()), last_hashes: RwLock::new(VecDeque::new()), factories: factories, history: history, @@ -826,8 +841,11 @@ impl Client { self.notify.write().push(Arc::downgrade(&target)); } - /// Set a closure to call when we want to restart the client - pub fn set_exit_handler(&self, f: F) where F: Fn(bool, Option) + 'static + Send { + /// Set a closure to call when the client wants to be restarted. + /// + /// The parameter passed to the callback is the name of the new chain spec to use after + /// the restart. + pub fn set_exit_handler(&self, f: F) where F: Fn(String) + 'static + Send { *self.exit_handler.lock() = Some(Box::new(f)); } @@ -837,7 +855,7 @@ impl Client { } fn notify(&self, f: F) where F: Fn(&ChainNotify) { - for np in self.notify.read().iter() { + for np in &*self.notify.read() { if let Some(n) = np.upgrade() { f(&*n); } @@ -904,7 +922,6 @@ impl Client { Arc::new(last_hashes) } - /// This is triggered by a message coming from a block queue when the block is ready for insertion pub fn import_verified_blocks(&self) -> usize { self.importer.import_verified_blocks(self) @@ -971,21 +988,8 @@ impl Client { } } - /// Import transactions from the IO queue - pub fn import_queued_transactions(&self, transactions: &[Bytes], peer_id: usize) -> usize { - trace!(target: "external_tx", "Importing queued"); - trace_time!("import_queued_transactions"); - self.queue_transactions.fetch_sub(transactions.len(), AtomicOrdering::SeqCst); - let txs: Vec = transactions.iter().filter_map(|bytes| UntrustedRlp::new(bytes).as_val().ok()).collect(); - let hashes: Vec<_> = txs.iter().map(|tx| tx.hash()).collect(); - self.notify(|notify| { - notify.transactions_received(hashes.clone(), peer_id); - }); - let results = self.importer.miner.import_external_transactions(self, txs); - results.len() - } - /// Get shared miner reference. + #[cfg(test)] pub fn miner(&self) -> Arc { self.importer.miner.clone() } @@ -1000,7 +1004,7 @@ impl Client { let header = self.best_block_header(); State::from_existing( self.state_db.read().boxed_clone_canon(&header.hash()), - header.state_root(), + *header.state_root(), self.engine.account_start_nonce(header.number()), self.factories.clone() ) @@ -1043,7 +1047,8 @@ impl Client { /// Otherwise, this can fail (but may not) if the DB prunes state. pub fn state_at_beginning(&self, id: BlockId) -> Option> { match self.block_number(id) { - None | Some(0) => None, + None => None, + Some(0) => self.state_at(id), Some(n) => self.state_at(BlockId::Number(n - 1)), } } @@ -1260,6 +1265,22 @@ impl Client { BlockId::Latest => Some(self.chain.read().best_block_number()), } } + + /// Retrieve a decoded header given `BlockId` + /// + /// This method optimizes access patterns for latest block header + /// to avoid excessive RLP encoding, decoding and hashing. + fn block_header_decoded(&self, id: BlockId) -> Option
{ + match id { + BlockId::Latest + => Some(self.chain.read().best_block_header()), + BlockId::Hash(ref hash) if hash == &self.chain.read().best_block_hash() + => Some(self.chain.read().best_block_header()), + BlockId::Number(number) if number == self.chain.read().best_block_number() + => Some(self.chain.read().best_block_header()), + _ => self.block_header(id).and_then(|h| h.decode().ok()) + } + } } impl snapshot::DatabaseRestore for Client { @@ -1315,16 +1336,14 @@ impl BlockInfo for Client { Self::block_hash(&chain, id).and_then(|hash| chain.block_header_data(&hash)) } - fn best_block_header(&self) -> encoded::Header { + fn best_block_header(&self) -> Header { self.chain.read().best_block_header() } fn block(&self, id: BlockId) -> Option { let chain = self.chain.read(); - Self::block_hash(&chain, id).and_then(|hash| { - chain.block(&hash) - }) + Self::block_hash(&chain, id).and_then(|hash| chain.block(&hash)) } fn code_hash(&self, address: &Address, id: BlockId) -> Option { @@ -1360,11 +1379,11 @@ impl CallContract for Client { fn call_contract(&self, block_id: BlockId, address: Address, data: Bytes) -> Result { let state_pruned = || CallError::StatePruned.to_string(); let state = &mut self.state_at(block_id).ok_or_else(&state_pruned)?; - let header = self.block_header(block_id).ok_or_else(&state_pruned)?; + let header = self.block_header_decoded(block_id).ok_or_else(&state_pruned)?; let transaction = self.contract_call_tx(block_id, address, data); - self.call(&transaction, Default::default(), state, &header.decode()) + self.call(&transaction, Default::default(), state, &header) .map_err(|e| format!("{:?}", e)) .map(|executed| executed.output) } @@ -1376,35 +1395,19 @@ impl ImportBlock for Client { use verification::queue::kind::blocks::Unverified; // create unverified block here so the `keccak` calculation can be cached. - let unverified = Unverified::new(bytes); + let unverified = Unverified::from_rlp(bytes)?; { if self.chain.read().is_known(&unverified.hash()) { - return Err(BlockImportError::Import(ImportError::AlreadyInChain)); + bail!(BlockImportErrorKind::Import(ImportErrorKind::AlreadyInChain)); } let status = self.block_status(BlockId::Hash(unverified.parent_hash())); if status == BlockStatus::Unknown || status == BlockStatus::Pending { - return Err(BlockImportError::Block(BlockError::UnknownParent(unverified.parent_hash()))); + bail!(BlockImportErrorKind::Block(BlockError::UnknownParent(unverified.parent_hash()))); } } Ok(self.importer.block_queue.import(unverified)?) } - - fn import_block_with_receipts(&self, block_bytes: Bytes, receipts_bytes: Bytes) -> Result { - { - // check block order - let header = BlockView::new(&block_bytes).header_view(); - if self.chain.read().is_known(&header.hash()) { - return Err(BlockImportError::Import(ImportError::AlreadyInChain)); - } - let status = self.block_status(BlockId::Hash(header.parent_hash())); - if status == BlockStatus::Unknown || status == BlockStatus::Pending { - return Err(BlockImportError::Block(BlockError::UnknownParent(header.parent_hash()))); - } - } - - self.importer.import_old_block(block_bytes, receipts_bytes, &**self.db.read(), &*self.chain.read()).map_err(Into::into) - } } impl StateClient for Client { @@ -1461,7 +1464,7 @@ impl Call for Client { } fn estimate_gas(&self, t: &SignedTransaction, state: &Self::State, header: &Header) -> Result { - let (mut upper, max_upper, env_info) = { + let (mut upper, max_upper, env_info) = { let init = *header.gas_limit(); let max = init * U256::from(10); @@ -1567,20 +1570,19 @@ impl BlockChainClient for Client { }))) } - - fn mode(&self) -> IpcMode { + fn mode(&self) -> Mode { let r = self.mode.lock().clone().into(); trace!(target: "mode", "Asked for mode = {:?}. returning {:?}", &*self.mode.lock(), r); r } fn disable(&self) { - self.set_mode(IpcMode::Off); + self.set_mode(Mode::Off); self.enabled.store(false, AtomicOrdering::Relaxed); self.clear_queue(); } - fn set_mode(&self, new_mode: IpcMode) { + fn set_mode(&self, new_mode: Mode) { trace!(target: "mode", "Client::set_mode({:?})", new_mode); if !self.enabled.load(AtomicOrdering::Relaxed) { return; @@ -1595,8 +1597,8 @@ impl BlockChainClient for Client { } } match new_mode { - IpcMode::Active => self.wake_up(), - IpcMode::Off => self.sleep(), + Mode::Active => self.wake_up(), + Mode::Off => self.sleep(), _ => {(*self.sleep_state.lock()).last_activity = Some(Instant::now()); } } } @@ -1611,7 +1613,7 @@ impl BlockChainClient for Client { return; } if let Some(ref h) = *self.exit_handler.lock() { - (*h)(true, Some(new_spec_name)); + (*h)(new_spec_name); } else { warn!("Not hypervised; cannot change chain."); } @@ -1816,26 +1818,89 @@ impl BlockChainClient for Client { } fn logs(&self, filter: Filter) -> Vec { - let (from, to) = match (self.block_number_ref(&filter.from_block), self.block_number_ref(&filter.to_block)) { - (Some(from), Some(to)) => (from, to), - _ => return Vec::new(), - }; + // Wrap the logic inside a closure so that we can take advantage of question mark syntax. + let fetch_logs = || { + let chain = self.chain.read(); - let chain = self.chain.read(); - let blocks = filter.bloom_possibilities().iter() - .map(move |bloom| { - chain.blocks_with_bloom(bloom, from, to) - }) - .flat_map(|m| m) - // remove duplicate elements - .collect::>() - .into_iter() - .collect::>(); + // First, check whether `filter.from_block` and `filter.to_block` is on the canon chain. If so, we can use the + // optimized version. + let is_canon = |id| { + match id { + // If it is referred by number, then it is always on the canon chain. + &BlockId::Earliest | &BlockId::Latest | &BlockId::Number(_) => true, + // If it is referred by hash, we see whether a hash -> number -> hash conversion gives us the same + // result. + &BlockId::Hash(ref hash) => chain.is_canon(hash), + } + }; + + let blocks = if is_canon(&filter.from_block) && is_canon(&filter.to_block) { + // If we are on the canon chain, use bloom filter to fetch required hashes. + let from = self.block_number_ref(&filter.from_block)?; + let to = self.block_number_ref(&filter.to_block)?; + + filter.bloom_possibilities().iter() + .map(|bloom| { + chain.blocks_with_bloom(bloom, from, to) + }) + .flat_map(|m| m) + // remove duplicate elements + .collect::>() + .into_iter() + .filter_map(|n| chain.block_hash(n)) + .collect::>() + + } else { + // Otherwise, we use a slower version that finds a link between from_block and to_block. + let from_hash = Self::block_hash(&chain, filter.from_block)?; + let from_number = chain.block_number(&from_hash)?; + let to_hash = Self::block_hash(&chain, filter.from_block)?; + + let blooms = filter.bloom_possibilities(); + let bloom_match = |header: &encoded::Header| { + blooms.iter().any(|bloom| header.log_bloom().contains_bloom(bloom)) + }; - self.chain.read().logs(blocks, |entry| filter.matches(entry), filter.limit) + let (blocks, last_hash) = { + let mut blocks = Vec::new(); + let mut current_hash = to_hash; + + loop { + let header = chain.block_header_data(¤t_hash)?; + if bloom_match(&header) { + blocks.push(current_hash); + } + + // Stop if `from` block is reached. + if header.number() <= from_number { + break; + } + current_hash = header.parent_hash(); + } + + blocks.reverse(); + (blocks, current_hash) + }; + + // Check if we've actually reached the expected `from` block. + if last_hash != from_hash || blocks.is_empty() { + return None; + } + + blocks + }; + + Some(self.chain.read().logs(blocks, |entry| filter.matches(entry), filter.limit)) + }; + + fetch_logs().unwrap_or_default() } fn filter_traces(&self, filter: TraceFilter) -> Option> { + if !self.tracedb.read().tracing_enabled() { + return None; + } + let start = self.block_number(filter.range.start)?; let end = self.block_number(filter.range.end)?; @@ -1855,6 +1920,10 @@ impl BlockChainClient for Client { } fn trace(&self, trace: TraceId) -> Option { + if !self.tracedb.read().tracing_enabled() { + return None; + } + let trace_address = trace.address; self.transaction_address(trace.transaction) .and_then(|tx_address| { @@ -1864,6 +1933,10 @@ impl BlockChainClient for Client { } fn transaction_traces(&self, transaction: TransactionId) -> Option> { + if !self.tracedb.read().tracing_enabled() { + return None; + } + self.transaction_address(transaction) .and_then(|tx_address| { self.block_number(BlockId::Hash(tx_address.block_hash)) @@ -1872,6 +1945,10 @@ impl BlockChainClient for Client { } fn block_traces(&self, block: BlockId) -> Option> { + if !self.tracedb.read().tracing_enabled() { + return None; + } + self.block_number(block) .and_then(|number| self.tracedb.read().block_traces(number)) } @@ -1880,37 +1957,8 @@ impl BlockChainClient for Client { (*self.build_last_hashes(&self.chain.read().best_block_hash())).clone() } - fn queue_transactions(&self, transactions: Vec, peer_id: usize) { - let queue_size = self.queue_transactions.load(AtomicOrdering::Relaxed); - trace!(target: "external_tx", "Queue size: {}", queue_size); - if queue_size > MAX_TX_QUEUE_SIZE { - debug!("Ignoring {} transactions: queue is full", transactions.len()); - } else { - let len = transactions.len(); - match self.io_channel.lock().send(ClientIoMessage::NewTransactions(transactions, peer_id)) { - Ok(_) => { - self.queue_transactions.fetch_add(len, AtomicOrdering::SeqCst); - } - Err(e) => { - debug!("Ignoring {} transactions: error queueing: {}", len, e); - } - } - } - } - - fn ready_transactions(&self) -> Vec { - let (number, timestamp) = { - let chain = self.chain.read(); - (chain.best_block_number(), chain.best_block_timestamp()) - }; - self.importer.miner.ready_transactions(number, timestamp) - } - - fn queue_consensus_message(&self, message: Bytes) { - let channel = self.io_channel.lock().clone(); - if let Err(e) = channel.send(ClientIoMessage::NewMessage(message)) { - debug!("Ignoring the message, error queueing: {}", e); - } + fn ready_transactions(&self, max_len: usize) -> Vec> { + self.importer.miner.ready_transactions(self, max_len, ::miner::PendingOrdering::Priority) } fn signing_chain_id(&self) -> Option { @@ -1918,13 +1966,17 @@ impl BlockChainClient for Client { } fn block_extra_info(&self, id: BlockId) -> Option> { - self.block_header(id) - .map(|header| self.engine.extra_info(&header.decode())) + self.block_header_decoded(id) + .map(|header| self.engine.extra_info(&header)) } fn uncle_extra_info(&self, id: UncleId) -> Option> { self.uncle(id) - .map(|header| self.engine.extra_info(&header.decode())) + .and_then(|h| { + h.decode().map(|dh| { + self.engine.extra_info(&dh) + }).ok() + }) } fn pruning_info(&self) -> PruningInfo { @@ -1934,17 +1986,19 @@ impl BlockChainClient for Client { } } - fn transact_contract(&self, address: Address, data: Bytes) -> Result { + fn transact_contract(&self, address: Address, data: Bytes) -> Result<(), transaction::Error> { + let authoring_params = self.importer.miner.authoring_params(); let transaction = Transaction { - nonce: self.latest_nonce(&self.importer.miner.author()), + nonce: self.latest_nonce(&authoring_params.author), action: Action::Call(address), - gas: self.importer.miner.gas_floor_target(), + gas: self.importer.miner.sensible_gas_limit(), gas_price: self.importer.miner.sensible_gas_price(), value: U256::zero(), data: data, }; let chain_id = self.engine.signing_chain_id(&self.latest_env_info()); - let signature = self.engine.sign(transaction.hash(chain_id))?; + let signature = self.engine.sign(transaction.hash(chain_id)) + .map_err(|e| transaction::Error::InvalidSignature(e.to_string()))?; let signed = SignedTransaction::new(transaction.with_signature(signature, chain_id))?; self.importer.miner.import_own_transaction(self, signed.into()) } @@ -1958,6 +2012,104 @@ impl BlockChainClient for Client { } } +impl IoClient for Client { + fn queue_transactions(&self, transactions: Vec, peer_id: usize) { + trace_time!("queue_transactions"); + let len = transactions.len(); + self.queue_transactions.queue(&mut self.io_channel.lock(), len, move |client| { + trace_time!("import_queued_transactions"); + + let txs: Vec = transactions + .iter() + .filter_map(|bytes| client.engine.decode_transaction(bytes).ok()) + .collect(); + + client.notify(|notify| { + notify.transactions_received(&txs, peer_id); + }); + + client.importer.miner.import_external_transactions(client, txs); + }).unwrap_or_else(|e| { + debug!(target: "client", "Ignoring {} transactions: {}", len, e); + }); + } + + fn queue_ancient_block(&self, block_bytes: Bytes, receipts_bytes: Bytes) -> Result { + trace_time!("queue_ancient_block"); + let header: Header = ::rlp::Rlp::new(&block_bytes).val_at(0)?; + let hash = header.hash(); + + { + // check block order + if self.chain.read().is_known(&hash) { + bail!(BlockImportErrorKind::Import(ImportErrorKind::AlreadyInChain)); + } + let parent_hash = header.parent_hash(); + // NOTE To prevent race condition with import, make sure to check queued blocks first + // (and attempt to acquire lock) + let is_parent_pending = self.queued_ancient_blocks.read().0.contains(parent_hash); + if !is_parent_pending { + let status = self.block_status(BlockId::Hash(*parent_hash)); + if status == BlockStatus::Unknown || status == BlockStatus::Pending { + bail!(BlockImportErrorKind::Block(BlockError::UnknownParent(*parent_hash))); + } + } + } + + // we queue blocks here and trigger an IO message. + { + let mut queued = self.queued_ancient_blocks.write(); + queued.0.insert(hash); + queued.1.push_back((header, block_bytes, receipts_bytes)); + } + + let queued = self.queued_ancient_blocks.clone(); + let lock = self.ancient_blocks_import_lock.clone(); + match self.queue_ancient_blocks.queue(&mut self.io_channel.lock(), 1, move |client| { + trace_time!("import_ancient_block"); + // Make sure to hold the lock here to prevent importing out of order. + // We use separate lock, cause we don't want to block queueing. + let _lock = lock.lock(); + for _i in 0..MAX_ANCIENT_BLOCKS_TO_IMPORT { + let first = queued.write().1.pop_front(); + if let Some((header, block_bytes, receipts_bytes)) = first { + let hash = header.hash(); + let result = client.importer.import_old_block( + &header, + &block_bytes, + &receipts_bytes, + &**client.db.read(), + &*client.chain.read(), + ); + if let Err(e) = result { + error!(target: "client", "Error importing ancient block: {}", e); + } + // remove from pending + queued.write().0.remove(&hash); + } else { + break; + } + } + }) { + Ok(_) => Ok(hash), + Err(e) => bail!(BlockImportErrorKind::Other(format!("{}", e))), + } + } + + fn queue_consensus_message(&self, message: Bytes) { + match self.queue_consensus_message.queue(&mut self.io_channel.lock(), 1, move |client| { + if let Err(e) = client.engine().handle_message(&message) { + debug!(target: "poa", "Invalid message received: {}", e); + } + }) { + Ok(_) => (), + Err(e) => { + debug!(target: "poa", "Ignoring the message, error queueing: {}", e); + } + } + } +} + impl ReopenBlock for Client { fn reopen_block(&self, block: ClosedBlock) -> OpenBlock { let engine = &*self.engine; @@ -1973,7 +2125,8 @@ impl ReopenBlock for Client { for h in uncles { if !block.uncles().iter().any(|header| header.hash() == h) { - let uncle = chain.block_header(&h).expect("find_uncle_hashes only returns hashes for existing headers; qed"); + let uncle = chain.block_header_data(&h).expect("find_uncle_hashes only returns hashes for existing headers; qed"); + let uncle = uncle.decode().expect("decoding failure"); block.push_uncle(uncle).expect("pushing up to maximum_uncle_count; push_uncle is not ok only if more than maximum_uncle_count is pushed; so all push_uncle are Ok; @@ -1991,9 +2144,8 @@ impl PrepareOpenBlock for Client { fn prepare_open_block(&self, author: Address, gas_range_target: (U256, U256), extra_data: Bytes) -> OpenBlock { let engine = &*self.engine; let chain = self.chain.read(); - let h = chain.best_block_hash(); - let best_header = &chain.block_header(&h) - .expect("h is best block hash: so its header must exist: qed"); + let best_header = chain.best_block_header(); + let h = best_header.hash(); let is_epoch_begin = chain.epoch_transition(best_header.number(), h).is_some(); let mut open_block = OpenBlock::new( @@ -2001,12 +2153,13 @@ impl PrepareOpenBlock for Client { self.factories.clone(), self.tracedb.read().tracing_enabled(), self.state_db.read().boxed_clone_canon(&h), - best_header, + &best_header, self.build_last_hashes(&h), author, gas_range_target, extra_data, is_epoch_begin, + &mut chain.ancestry_with_metadata_iter(best_header.hash()), ).expect("OpenBlock::new only fails if parent state root invalid; state root of best block's header is never invalid; qed"); // Add uncles @@ -2016,7 +2169,7 @@ impl PrepareOpenBlock for Client { .into_iter() .take(engine.maximum_uncle_count(open_block.header().number())) .foreach(|h| { - open_block.push_uncle(h).expect("pushing maximum_uncle_count; + open_block.push_uncle(h.decode().expect("decoding failure")).expect("pushing maximum_uncle_count; open_block was just created; push_uncle is not ok only if more than maximum_uncle_count is pushed; so all push_uncle are Ok; @@ -2053,20 +2206,16 @@ impl ImportSealedBlock for Client { self.state_db.write().sync_cache(&route.enacted, &route.retracted, false); route }; - let (enacted, retracted) = self.importer.calculate_enacted_retracted(&[route]); - self.importer.miner.chain_new_blocks(self, &[h.clone()], &[], &enacted, &retracted); + let route = ChainRoute::from([route].as_ref()); + self.importer.miner.chain_new_blocks(self, &[h.clone()], &[], route.enacted(), route.retracted(), self.engine.seals_internally().is_some()); self.notify(|notify| { notify.new_blocks( vec![h.clone()], vec![], - enacted.clone(), - retracted.clone(), + route.clone(), vec![h.clone()], vec![], - { - let elapsed = start.elapsed(); - elapsed.as_secs() * 1_000_000_000 + elapsed.subsec_nanos() as u64 - }, + start.elapsed(), ); }); self.db.read().flush().expect("DB flush failed."); @@ -2076,15 +2225,15 @@ impl ImportSealedBlock for Client { impl BroadcastProposalBlock for Client { fn broadcast_proposal_block(&self, block: SealedBlock) { + const DURATION_ZERO: Duration = Duration::from_millis(0); self.notify(|notify| { notify.new_blocks( vec![], vec![], - vec![], - vec![], + ChainRoute::default(), vec![], vec![block.rlp_bytes()], - 0, + DURATION_ZERO, ); }); } @@ -2092,11 +2241,8 @@ impl BroadcastProposalBlock for Client { impl SealedBlockImporter for Client {} -impl MiningBlockChainClient for Client { - fn vm_factory(&self) -> &VmFactory { - &self.factories.vm - } -} +impl ::miner::TransactionVerifierClient for Client {} +impl ::miner::BlockChainClient for Client {} impl super::traits::EngineClient for Client { fn update_sealing(&self) { @@ -2104,13 +2250,14 @@ impl super::traits::EngineClient for Client { } fn submit_seal(&self, block_hash: H256, seal: Vec) { - if self.importer.miner.submit_seal(self, block_hash, seal).is_err() { - warn!(target: "poa", "Wrong internal seal submission!") + let import = self.importer.miner.submit_seal(block_hash, seal).and_then(|block| self.import_sealed_block(block)); + if let Err(err) = import { + warn!(target: "poa", "Wrong internal seal submission! {:?}", err); } } fn broadcast_consensus_message(&self, message: Bytes) { - self.notify(|notify| notify.broadcast(message.clone())); + self.notify(|notify| notify.broadcast(ChainMessageType::Consensus(message.clone()))); } fn epoch_transition_for(&self, parent_hash: H256) -> Option<::engines::EpochTransition> { @@ -2159,7 +2306,6 @@ impl ProvingBlockChainClient for Client { ) } - fn epoch_signal(&self, hash: H256) -> Option> { // pending transitions are never deleted, and do not contain // finality proofs by definition. @@ -2191,6 +2337,11 @@ fn transaction_receipt(machine: &::machine::EthereumMachine, mut tx: LocalizedTr let transaction_index = tx.transaction_index; LocalizedReceipt { + from: sender, + to: match tx.action { + Action::Create => None, + Action::Call(ref address) => Some(address.clone().into()) + }, transaction_hash: transaction_hash, transaction_index: transaction_index, block_hash: block_hash, @@ -2221,13 +2372,14 @@ mod tests { #[test] fn should_not_cache_details_before_commit() { use client::{BlockChainClient, ChainInfo}; - use tests::helpers::{generate_dummy_client, get_good_dummy_block_hash}; + use test_helpers::{generate_dummy_client, get_good_dummy_block_hash}; use std::thread; use std::time::Duration; use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; use kvdb::DBTransaction; + use blockchain::ExtrasInsert; let client = generate_dummy_client(0); let genesis = client.chain_info().best_block_hash; @@ -2240,7 +2392,11 @@ mod tests { let another_client = client.clone(); thread::spawn(move || { let mut batch = DBTransaction::new(); - another_client.chain.read().insert_block(&mut batch, &new_block, Vec::new()); + another_client.chain.read().insert_block(&mut batch, &new_block, Vec::new(), ExtrasInsert { + fork_choice: ::engines::ForkChoice::New, + is_finalized: false, + metadata: None, + }); go_thread.store(true, Ordering::SeqCst); }); go @@ -2311,6 +2467,11 @@ mod tests { // then assert_eq!(receipt, LocalizedReceipt { + from: tx1.sender().into(), + to: match tx1.action { + Action::Create => None, + Action::Call(ref address) => Some(address.clone().into()) + }, transaction_hash: tx1.hash(), transaction_index: 1, block_hash: block_hash, @@ -2340,3 +2501,54 @@ mod tests { }); } } + +#[derive(Debug)] +enum QueueError { + Channel(IoError), + Full(usize), +} + +impl fmt::Display for QueueError { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + match *self { + QueueError::Channel(ref c) => fmt::Display::fmt(c, fmt), + QueueError::Full(limit) => write!(fmt, "The queue is full ({})", limit), + } + } +} + +/// Queue some items to be processed by IO client. +struct IoChannelQueue { + currently_queued: Arc, + limit: usize, +} + +impl IoChannelQueue { + pub fn new(limit: usize) -> Self { + IoChannelQueue { + currently_queued: Default::default(), + limit, + } + } + + pub fn queue(&self, channel: &mut IoChannel, count: usize, fun: F) -> Result<(), QueueError> where + F: Fn(&Client) + Send + Sync + 'static, + { + let queue_size = self.currently_queued.load(AtomicOrdering::Relaxed); + ensure!(queue_size < self.limit, QueueError::Full(self.limit)); + + let currently_queued = self.currently_queued.clone(); + let result = channel.send(ClientIoMessage::execute(move |client| { + currently_queued.fetch_sub(count, AtomicOrdering::SeqCst); + fun(client); + })); + + match result { + Ok(_) => { + self.currently_queued.fetch_add(count, AtomicOrdering::SeqCst); + Ok(()) + }, + Err(e) => Err(QueueError::Channel(e)), + } + } +} diff --git a/ethcore/src/client/config.rs b/ethcore/src/client/config.rs index 3c26c462123c50babb53f02f4cdf3cd83d7d2eb8..dbba4f4a969d9337c08e0e7bad25716866aa15a8 100644 --- a/ethcore/src/client/config.rs +++ b/ethcore/src/client/config.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -15,13 +15,10 @@ // along with Parity. If not, see . use std::str::FromStr; -use std::path::Path; use std::fmt::{Display, Formatter, Error as FmtError}; -use mode::Mode as IpcMode; use verification::{VerifierType, QueueConfig}; use journaldb; -use kvdb_rocksdb::CompactionProfile; pub use std::time::Duration; pub use blockchain::Config as BlockChainConfig; @@ -45,17 +42,6 @@ impl Default for DatabaseCompactionProfile { } } -impl DatabaseCompactionProfile { - /// Returns corresponding compaction profile. - pub fn compaction_profile(&self, db_path: &Path) -> CompactionProfile { - match *self { - DatabaseCompactionProfile::Auto => CompactionProfile::auto(db_path), - DatabaseCompactionProfile::SSD => CompactionProfile::ssd(), - DatabaseCompactionProfile::HDD => CompactionProfile::hdd(), - } - } -} - impl FromStr for DatabaseCompactionProfile { type Err = String; @@ -74,10 +60,10 @@ impl FromStr for DatabaseCompactionProfile { pub enum Mode { /// Always on. Active, - /// Goes offline after RLP is inactive for some (given) time, but + /// Goes offline after client is inactive for some (given) time, but /// comes back online after a while of inactivity. Passive(Duration, Duration), - /// Goes offline after RLP is inactive for some (given) time and + /// Goes offline after client is inactive for some (given) time and /// stays inactive. Dark(Duration), /// Always off. @@ -101,29 +87,6 @@ impl Display for Mode { } } -impl Into for Mode { - fn into(self) -> IpcMode { - match self { - Mode::Off => IpcMode::Off, - Mode::Dark(timeout) => IpcMode::Dark(timeout.as_secs()), - Mode::Passive(timeout, alarm) => IpcMode::Passive(timeout.as_secs(), alarm.as_secs()), - Mode::Active => IpcMode::Active, - } - } -} - -impl From for Mode { - fn from(mode: IpcMode) -> Self { - match mode { - IpcMode::Off => Mode::Off, - IpcMode::Dark(timeout) => Mode::Dark(Duration::from_secs(timeout)), - IpcMode::Passive(timeout, alarm) => Mode::Passive(Duration::from_secs(timeout), Duration::from_secs(alarm)), - IpcMode::Active => Mode::Active, - } - } -} - - /// Client configuration. Includes configs for all sub-systems. #[derive(Debug, PartialEq, Default)] pub struct ClientConfig { diff --git a/ethcore/src/client/error.rs b/ethcore/src/client/error.rs index d2af13a3b27f8da862c0431c7add05e4083f51f3..0e6608c0ffae25d51ddbfec800d40cb8544f5a1d 100644 --- a/ethcore/src/client/error.rs +++ b/ethcore/src/client/error.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/src/client/evm_test_client.rs b/ethcore/src/client/evm_test_client.rs index b91414ca8fa69ce85072908abece3bc300ffa07a..fbf57cbdc725141d5ba7af351a0e1af35ae7c1ec 100644 --- a/ethcore/src/client/evm_test_client.rs +++ b/ethcore/src/client/evm_test_client.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/src/client/io_message.rs b/ethcore/src/client/io_message.rs index c2823a39f652d7d18fe7fda9860393dd4f141f69..d388f5ed44c3f2b9c7dd3da1f867e797048273b8 100644 --- a/ethcore/src/client/io_message.rs +++ b/ethcore/src/client/io_message.rs @@ -14,19 +14,19 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use ethereum_types::H256; +use std::fmt; use bytes::Bytes; +use client::Client; +use ethereum_types::H256; use snapshot::ManifestData; /// Message type for external and internal events -#[derive(Clone, PartialEq, Eq, Debug)] +#[derive(Debug)] pub enum ClientIoMessage { /// Best Block Hash in chain has been changed NewChainHead, /// A block is ready BlockVerified, - /// New transaction RLPs are ready to be imported - NewTransactions(Vec, usize), /// Begin snapshot restoration BeginRestoration(ManifestData), /// Feed a state chunk to the snapshot service @@ -35,7 +35,22 @@ pub enum ClientIoMessage { FeedBlockChunk(H256, Bytes), /// Take a snapshot for the block with given number. TakeSnapshot(u64), - /// New consensus message received. - NewMessage(Bytes) + /// Execute wrapped closure + Execute(Callback), +} + +impl ClientIoMessage { + /// Create new `ClientIoMessage` that executes given procedure. + pub fn execute(fun: F) -> Self { + ClientIoMessage::Execute(Callback(Box::new(fun))) + } } +/// A function to invoke in the client thread. +pub struct Callback(pub Box); + +impl fmt::Debug for Callback { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "") + } +} diff --git a/ethcore/src/client/mod.rs b/ethcore/src/client/mod.rs index b92240591b5c5af1f5e81e24050f10972f3dc96b..8c5abf3f5d972364a461f1c387eda469608a57b1 100644 --- a/ethcore/src/client/mod.rs +++ b/ethcore/src/client/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -20,24 +20,28 @@ mod ancient_import; mod client; mod config; mod error; +#[cfg(any(test, feature = "test-helpers"))] mod evm_test_client; mod io_message; +#[cfg(any(test, feature = "test-helpers"))] mod test_client; mod trace; pub use self::client::*; pub use self::config::{Mode, ClientConfig, DatabaseCompactionProfile, BlockChainConfig, VMType}; pub use self::error::Error; +#[cfg(any(test, feature = "test-helpers"))] pub use self::evm_test_client::{EvmTestClient, EvmTestError, TransactResult}; pub use self::io_message::ClientIoMessage; +#[cfg(any(test, feature = "test-helpers"))] pub use self::test_client::{TestBlockChainClient, EachBlockWith}; -pub use self::chain_notify::ChainNotify; +pub use self::chain_notify::{ChainNotify, ChainRoute, ChainRouteType, ChainMessageType}; pub use self::traits::{ Nonce, Balance, ChainInfo, BlockInfo, ReopenBlock, PrepareOpenBlock, CallContract, TransactionInfo, RegistryInfo, ScheduleInfo, ImportSealedBlock, BroadcastProposalBlock, ImportBlock, StateOrBlock, StateClient, Call, EngineInfo, AccountData, BlockChain, BlockProducer, SealedBlockImporter }; pub use state::StateInfo; -pub use self::traits::{BlockChainClient, MiningBlockChainClient, EngineClient, ProvingBlockChainClient}; +pub use self::traits::{BlockChainClient, EngineClient, ProvingBlockChainClient, IoClient}; pub use types::ids::*; pub use types::trace_filter::Filter as TraceFilter; @@ -47,9 +51,10 @@ pub use types::call_analytics::CallAnalytics; pub use executive::{Executed, Executive, TransactOptions}; pub use vm::{LastHashes, EnvInfo}; -pub use error::{BlockImportError, TransactionImportError}; +pub use error::{BlockImportError, BlockImportErrorKind, TransactionImportError}; pub use verification::VerifierType; mod traits; mod chain_notify; +mod private_notify; diff --git a/ethcore/types/src/mode.rs b/ethcore/src/client/private_notify.rs similarity index 61% rename from ethcore/types/src/mode.rs rename to ethcore/src/client/private_notify.rs index 539ebcdbd8ff0ffe01b2b616bf4d4433035ceb13..d1fde555c98be7ce6990ed69491e3613612c4c9b 100644 --- a/ethcore/types/src/mode.rs +++ b/ethcore/src/client/private_notify.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -14,19 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -//! Mode type +use error::TransactionImportError; -pub use std::time::Duration; - -/// IPC-capable shadow-type for `client::config::Mode` -#[derive(Clone, Debug)] -pub enum Mode { - /// Same as `ClientMode::Off`. - Off, - /// Same as `ClientMode::Dark`; values in seconds. - Dark(u64), - /// Same as `ClientMode::Passive`; values in seconds. - Passive(u64, u64), - /// Same as `ClientMode::Active`. - Active, +/// Represent private transactions handler inside the client +pub trait PrivateNotify : Send + Sync { + /// fires when private transaction message queued via client io queue + fn private_transaction_queued(&self) -> Result<(), TransactionImportError>; } diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index b806183a2c941d792a2135939a87015d597e4f98..620f1af33306b6f0e77363d2341b3f74b3ed3ca8 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -16,7 +16,7 @@ //! Test client. -use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrder}; +use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering as AtomicOrder}; use std::sync::Arc; use std::collections::{HashMap, BTreeMap}; use std::mem; @@ -29,30 +29,28 @@ use journaldb; use kvdb::DBValue; use kvdb_memorydb; use bytes::Bytes; -use rlp::*; +use rlp::{Rlp, RlpStream}; use ethkey::{Generator, Random}; -use transaction::{self, Transaction, LocalizedTransaction, PendingTransaction, SignedTransaction, Action}; +use ethcore_miner::pool::VerifiedTransaction; +use transaction::{self, Transaction, LocalizedTransaction, SignedTransaction, Action}; use blockchain::{TreeRoute, BlockReceipts}; use client::{ Nonce, Balance, ChainInfo, BlockInfo, ReopenBlock, CallContract, TransactionInfo, RegistryInfo, - PrepareOpenBlock, BlockChainClient, MiningBlockChainClient, BlockChainInfo, BlockStatus, BlockId, + PrepareOpenBlock, BlockChainClient, BlockChainInfo, BlockStatus, BlockId, Mode, TransactionId, UncleId, TraceId, TraceFilter, LastHashes, CallAnalytics, BlockImportError, ProvingBlockChainClient, ScheduleInfo, ImportSealedBlock, BroadcastProposalBlock, ImportBlock, StateOrBlock, - Call, StateClient, EngineInfo, AccountData, BlockChain, BlockProducer, SealedBlockImporter + Call, StateClient, EngineInfo, AccountData, BlockChain, BlockProducer, SealedBlockImporter, IoClient }; use db::{NUM_COLUMNS, COL_STATE}; use header::{Header as BlockHeader, BlockNumber}; use filter::Filter; use log_entry::LocalizedLogEntry; use receipt::{Receipt, LocalizedReceipt, TransactionOutcome}; -use error::{ImportResult, Error as EthcoreError}; -use evm::VMType; -use factory::VmFactory; +use error::ImportResult; use vm::Schedule; -use miner::{Miner, MinerService}; +use miner::{self, Miner, MinerService}; use spec::Spec; use types::basic_account::BasicAccount; -use types::mode::Mode; use types::pruning_info::PruningInfo; use verification::queue::QueueInfo; @@ -66,6 +64,7 @@ use encoded; use engines::EthEngine; use trie; use state::StateInfo; +use views::BlockView; /// Test client. pub struct TestBlockChainClient { @@ -101,8 +100,6 @@ pub struct TestBlockChainClient { pub miner: Arc, /// Spec pub spec: Spec, - /// VM Factory - pub vm_factory: VmFactory, /// Timestamp assigned to latest sealed block pub latest_block_timestamp: RwLock, /// Ancient block info. @@ -113,6 +110,8 @@ pub struct TestBlockChainClient { pub traces: RwLock>>, /// Pruning history size to report. pub history: RwLock>, + /// Is disabled + pub disabled: AtomicBool, } /// Used for generating test client blocks. @@ -171,14 +170,14 @@ impl TestBlockChainClient { receipts: RwLock::new(HashMap::new()), logs: RwLock::new(Vec::new()), queue_size: AtomicUsize::new(0), - miner: Arc::new(Miner::with_spec(&spec)), + miner: Arc::new(Miner::new_for_tests(&spec, None)), spec: spec, - vm_factory: VmFactory::new(VMType::Interpreter, 1024 * 1024), latest_block_timestamp: RwLock::new(10_000_000), ancient_block: RwLock::new(None), first_block: RwLock::new(None), traces: RwLock::new(None), history: RwLock::new(None), + disabled: AtomicBool::new(false), }; // insert genesis hash. @@ -289,7 +288,7 @@ impl TestBlockChainClient { /// Make a bad block by setting invalid extra data. pub fn corrupt_block(&self, n: BlockNumber) { let hash = self.block_hash(BlockId::Number(n)).unwrap(); - let mut header: BlockHeader = self.block_header(BlockId::Number(n)).unwrap().decode(); + let mut header: BlockHeader = self.block_header(BlockId::Number(n)).unwrap().decode().expect("decoding failed"); header.set_extra_data(b"This extra data is way too long to be considered valid".to_vec()); let mut rlp = RlpStream::new_list(3); rlp.append(&header); @@ -301,7 +300,7 @@ impl TestBlockChainClient { /// Make a bad block by setting invalid parent hash. pub fn corrupt_block_parent(&self, n: BlockNumber) { let hash = self.block_hash(BlockId::Number(n)).unwrap(); - let mut header: BlockHeader = self.block_header(BlockId::Number(n)).unwrap().decode(); + let mut header: BlockHeader = self.block_header(BlockId::Number(n)).unwrap().decode().expect("decoding failed"); header.set_parent_hash(H256::from(42)); let mut rlp = RlpStream::new_list(3); rlp.append(&header); @@ -310,7 +309,7 @@ impl TestBlockChainClient { self.blocks.write().insert(hash, rlp.out()); } - /// TODO: + /// Get block hash with `delta` as offset from the most recent blocks. pub fn block_hash_delta_minus(&mut self, delta: usize) -> H256 { let blocks_read = self.numbers.read(); let index = blocks_read.len() - delta; @@ -341,8 +340,8 @@ impl TestBlockChainClient { self.set_balance(signed_tx.sender(), 10_000_000_000_000_000_000u64.into()); let hash = signed_tx.hash(); let res = self.miner.import_external_transactions(self, vec![signed_tx.into()]); - let res = res.into_iter().next().unwrap().expect("Successful import"); - assert_eq!(res, transaction::ImportResult::Current); + let res = res.into_iter().next().unwrap(); + assert!(res.is_ok()); hash } @@ -355,6 +354,11 @@ impl TestBlockChainClient { pub fn set_history(&self, h: Option) { *self.history.write() = h; } + + /// Returns true if the client has been disabled. + pub fn is_disabled(&self) -> bool { + self.disabled.load(AtomicOrder::Relaxed) + } } pub fn get_temp_state_db() -> StateDB { @@ -387,8 +391,9 @@ impl PrepareOpenBlock for TestBlockChainClient { gas_range_target, extra_data, false, + &mut Vec::new().into_iter(), ).expect("Opening block for tests will not fail."); - // TODO [todr] Override timestamp for predictability (set_timestamp_now kind of sucks) + // TODO [todr] Override timestamp for predictability open_block.set_timestamp(*self.latest_block_timestamp.read()); open_block } @@ -414,11 +419,8 @@ impl BroadcastProposalBlock for TestBlockChainClient { impl SealedBlockImporter for TestBlockChainClient {} -impl MiningBlockChainClient for TestBlockChainClient { - fn vm_factory(&self) -> &VmFactory { - &self.vm_factory - } -} +impl ::miner::TransactionVerifierClient for TestBlockChainClient {} +impl ::miner::BlockChainClient for TestBlockChainClient {} impl Nonce for TestBlockChainClient { fn nonce(&self, address: &Address, id: BlockId) -> Option { @@ -469,13 +471,15 @@ impl ChainInfo for TestBlockChainClient { impl BlockInfo for TestBlockChainClient { fn block_header(&self, id: BlockId) -> Option { self.block_hash(id) - .and_then(|hash| self.blocks.read().get(&hash).map(|r| Rlp::new(r).at(0).as_raw().to_vec())) + .and_then(|hash| self.blocks.read().get(&hash).map(|r| view!(BlockView, r).header_rlp().as_raw().to_vec())) .map(encoded::Header::new) } - fn best_block_header(&self) -> encoded::Header { + fn best_block_header(&self) -> Header { self.block_header(BlockId::Hash(self.chain_info().best_block_hash)) .expect("Best block always has header.") + .decode() + .expect("decoding failed") } fn block(&self, id: BlockId) -> Option { @@ -510,7 +514,7 @@ impl RegistryInfo for TestBlockChainClient { impl ImportBlock for TestBlockChainClient { fn import_block(&self, b: Bytes) -> Result { - let header = Rlp::new(&b).val_at::(0); + let header = view!(BlockView, &b).header(); let h = header.hash(); let number: usize = header.number() as usize; if number > self.blocks.read().len() { @@ -519,7 +523,7 @@ impl ImportBlock for TestBlockChainClient { if number > 0 { match self.blocks.read().get(header.parent_hash()) { Some(parent) => { - let parent = Rlp::new(parent).val_at::(0); + let parent = view!(BlockView, parent).header(); if parent.number() != (header.number() - 1) { panic!("Unexpected block parent"); } @@ -544,7 +548,7 @@ impl ImportBlock for TestBlockChainClient { while n > 0 && self.numbers.read()[&n] != parent_hash { *self.numbers.write().get_mut(&n).unwrap() = parent_hash.clone(); n -= 1; - parent_hash = Rlp::new(&self.blocks.read()[&parent_hash]).val_at::(0).parent_hash().clone(); + parent_hash = view!(BlockView, &self.blocks.read()[&parent_hash]).header().parent_hash().clone(); } } } @@ -553,10 +557,6 @@ impl ImportBlock for TestBlockChainClient { } Ok(h) } - - fn import_block_with_receipts(&self, b: Bytes, _r: Bytes) -> Result { - self.import_block(b) - } } impl Call for TestBlockChainClient { @@ -677,15 +677,22 @@ impl BlockChainClient for TestBlockChainClient { unimplemented!(); } - fn block_number(&self, _id: BlockId) -> Option { - unimplemented!() + fn block_number(&self, id: BlockId) -> Option { + match id { + BlockId::Number(number) => Some(number), + BlockId::Earliest => Some(0), + BlockId::Latest => Some(self.chain_info().best_block_number), + BlockId::Hash(ref h) => + self.numbers.read().iter().find(|&(_, hash)| hash == h).map(|e| *e.0 as u64) + } } fn block_body(&self, id: BlockId) -> Option { self.block_hash(id).and_then(|hash| self.blocks.read().get(&hash).map(|r| { + let block = view!(BlockView, r); let mut stream = RlpStream::new_list(2); - stream.append_raw(Rlp::new(r).at(1).as_raw(), 1); - stream.append_raw(Rlp::new(r).at(2).as_raw(), 1); + stream.append_raw(block.transactions_rlp().as_raw(), 1); + stream.append_raw(block.uncles_rlp().as_raw(), 1); encoded::Body::new(stream.out()) })) } @@ -696,7 +703,6 @@ impl BlockChainClient for TestBlockChainClient { .map(|header| self.spec.engine.extra_info(&header)) } - fn block_status(&self, id: BlockId) -> BlockStatus { match id { BlockId::Number(number) if (number as usize) < self.blocks.read().len() => BlockStatus::InChain, @@ -732,7 +738,8 @@ impl BlockChainClient for TestBlockChainClient { } } if adding { Vec::new() } else { blocks } - } + }, + is_from_route_finalized: false, }) } @@ -799,19 +806,8 @@ impl BlockChainClient for TestBlockChainClient { self.traces.read().clone() } - fn queue_transactions(&self, transactions: Vec, _peer_id: usize) { - // import right here - let txs = transactions.into_iter().filter_map(|bytes| UntrustedRlp::new(&bytes).as_val().ok()).collect(); - self.miner.import_external_transactions(self, txs); - } - - fn queue_consensus_message(&self, message: Bytes) { - self.spec.engine.handle_message(&message).unwrap(); - } - - fn ready_transactions(&self) -> Vec { - let info = self.chain_info(); - self.miner.ready_transactions(info.best_block_number, info.best_block_timestamp) + fn ready_transactions(&self, max_len: usize) -> Vec> { + self.miner.ready_transactions(self, max_len, miner::PendingOrdering::Priority) } fn signing_chain_id(&self) -> Option { None } @@ -824,7 +820,7 @@ impl BlockChainClient for TestBlockChainClient { fn set_spec_name(&self, _: String) { unimplemented!(); } - fn disable(&self) { unimplemented!(); } + fn disable(&self) { self.disabled.store(true, AtomicOrder::Relaxed); } fn pruning_info(&self) -> PruningInfo { let best_num = self.chain_info().best_block_number; @@ -834,9 +830,9 @@ impl BlockChainClient for TestBlockChainClient { } } - fn transact_contract(&self, address: Address, data: Bytes) -> Result { + fn transact_contract(&self, address: Address, data: Bytes) -> Result<(), transaction::Error> { let transaction = Transaction { - nonce: self.latest_nonce(&self.miner.author()), + nonce: self.latest_nonce(&self.miner.authoring_params().author), action: Action::Call(address), gas: self.spec.gas_limit, gas_price: U256::zero(), @@ -854,6 +850,22 @@ impl BlockChainClient for TestBlockChainClient { fn eip86_transition(&self) -> u64 { u64::max_value() } } +impl IoClient for TestBlockChainClient { + fn queue_transactions(&self, transactions: Vec, _peer_id: usize) { + // import right here + let txs = transactions.into_iter().filter_map(|bytes| Rlp::new(&bytes).as_val().ok()).collect(); + self.miner.import_external_transactions(self, txs); + } + + fn queue_ancient_block(&self, b: Bytes, _r: Bytes) -> Result { + self.import_block(b) + } + + fn queue_consensus_message(&self, message: Bytes) { + self.spec.engine.handle_message(&message).unwrap(); + } +} + impl ProvingBlockChainClient for TestBlockChainClient { fn prove_storage(&self, _: H256, _: H256, _: BlockId) -> Option<(Vec, H256)> { None @@ -878,8 +890,9 @@ impl super::traits::EngineClient for TestBlockChainClient { } fn submit_seal(&self, block_hash: H256, seal: Vec) { - if self.miner.submit_seal(self, block_hash, seal).is_err() { - warn!(target: "poa", "Wrong internal seal submission!") + let import = self.miner.submit_seal(block_hash, seal).and_then(|block| self.import_sealed_block(block)); + if let Err(err) = import { + warn!(target: "poa", "Wrong internal seal submission! {:?}", err); } } diff --git a/ethcore/src/client/trace.rs b/ethcore/src/client/trace.rs index 75e0fe34a1e383342e0cb344610df0f6a225792f..5f1b6c4f4de61ce65feabe909d08afb7c2b61c01 100644 --- a/ethcore/src/client/trace.rs +++ b/ethcore/src/client/trace.rs @@ -1,3 +1,18 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . //! Bridge between Tracedb and Blockchain. diff --git a/ethcore/src/client/traits.rs b/ethcore/src/client/traits.rs index 1228f87683c04cd97dffe957d591fd5ec50dd6c9..29876fe80a41f92cd543f55cf90f49e76e53be7e 100644 --- a/ethcore/src/client/traits.rs +++ b/ethcore/src/client/traits.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -15,28 +15,31 @@ // along with Parity. If not, see . use std::collections::BTreeMap; +use std::sync::Arc; + use itertools::Itertools; use block::{OpenBlock, SealedBlock, ClosedBlock}; use blockchain::TreeRoute; +use client::Mode; use encoded; use vm::LastHashes; -use error::{ImportResult, CallError, Error as EthcoreError, BlockImportError}; +use error::{ImportResult, CallError, BlockImportError}; use evm::Schedule; -use factory::VmFactory; use executive::Executed; use filter::Filter; use header::{BlockNumber}; use log_entry::LocalizedLogEntry; use receipt::LocalizedReceipt; use trace::LocalizedTrace; -use transaction::{LocalizedTransaction, PendingTransaction, SignedTransaction, ImportResult as TransactionImportResult}; +use transaction::{self, LocalizedTransaction, SignedTransaction}; use verification::queue::QueueInfo as BlockQueueInfo; use state::StateInfo; use header::Header; use engines::EthEngine; use ethereum_types::{H256, U256, Address}; +use ethcore_miner::pool::VerifiedTransaction; use bytes::Bytes; use hashdb::DBValue; @@ -46,7 +49,6 @@ use types::trace_filter::Filter as TraceFilter; use types::call_analytics::CallAnalytics; use types::blockchain_info::BlockChainInfo; use types::block_status::BlockStatus; -use types::mode::Mode; use types::pruning_info::PruningInfo; /// State information to be used during client query @@ -121,7 +123,7 @@ pub trait BlockInfo { fn block_header(&self, id: BlockId) -> Option; /// Get the best block header. - fn best_block_header(&self) -> encoded::Header; + fn best_block_header(&self) -> Header; /// Get raw block data by block header hash. fn block(&self, id: BlockId) -> Option; @@ -166,9 +168,6 @@ pub trait RegistryInfo { pub trait ImportBlock { /// Import a block into the blockchain. fn import_block(&self, bytes: Bytes) -> Result; - - /// Import a block with transaction receipts. Does no sealing and transaction validation. - fn import_block_with_receipts(&self, block_bytes: Bytes, receipts_bytes: Bytes) -> Result; } /// Provides `call_contract` method @@ -199,8 +198,21 @@ pub trait EngineInfo { fn engine(&self) -> &EthEngine; } +/// IO operations that should off-load heavy work to another thread. +pub trait IoClient: Sync + Send { + /// Queue transactions for importing. + fn queue_transactions(&self, transactions: Vec, peer_id: usize); + + /// Queue block import with transaction receipts. Does no sealing and transaction validation. + fn queue_ancient_block(&self, block_bytes: Bytes, receipts_bytes: Bytes) -> Result; + + /// Queue conensus engine message. + fn queue_consensus_message(&self, message: Bytes); +} + /// Blockchain database client. Owns and manages a blockchain and a block queue. -pub trait BlockChainClient : Sync + Send + AccountData + BlockChain + CallContract + RegistryInfo + ImportBlock { +pub trait BlockChainClient : Sync + Send + AccountData + BlockChain + CallContract + RegistryInfo + ImportBlock ++ IoClient { /// Look up the block number for the given block ID. fn block_number(&self, id: BlockId) -> Option; @@ -308,14 +320,8 @@ pub trait BlockChainClient : Sync + Send + AccountData + BlockChain + CallContra /// Get last hashes starting from best block. fn last_hashes(&self) -> LastHashes; - /// Queue transactions for importing. - fn queue_transactions(&self, transactions: Vec, peer_id: usize); - - /// Queue conensus engine message. - fn queue_consensus_message(&self, message: Bytes); - /// List all transactions that are allowed into the next block. - fn ready_transactions(&self) -> Vec; + fn ready_transactions(&self, max_len: usize) -> Vec>; /// Sorted list of transaction gas prices from at least last sample_size blocks. fn gas_price_corpus(&self, sample_size: usize) -> ::stats::Corpus { @@ -366,8 +372,8 @@ pub trait BlockChainClient : Sync + Send + AccountData + BlockChain + CallContra /// Returns information about pruning/data availability. fn pruning_info(&self) -> PruningInfo; - /// Import a transaction: used for misbehaviour reporting. - fn transact_contract(&self, address: Address, data: Bytes) -> Result; + /// Schedule state-altering transaction to be executed on the next pending block. + fn transact_contract(&self, address: Address, data: Bytes) -> Result<(), transaction::Error>; /// Get the address of the registry itself. fn registrar_address(&self) -> Option
; @@ -416,12 +422,6 @@ pub trait BroadcastProposalBlock { /// Provides methods to import sealed block and broadcast a block proposal pub trait SealedBlockImporter: ImportSealedBlock + BroadcastProposalBlock {} -/// Extended client interface used for mining -pub trait MiningBlockChainClient: BlockChainClient + BlockProducer + ScheduleInfo + SealedBlockImporter { - /// Returns EvmFactory. - fn vm_factory(&self) -> &VmFactory; -} - /// Client facilities used by internally sealing Engines. pub trait EngineClient: Sync + Send + ChainInfo { /// Make a new block and seal it. diff --git a/ethcore/src/db.rs b/ethcore/src/db.rs index d11adc7710d4d334efc99ee47256453d469e3261..39c30e9637601064bc8609a8b55e1c99a9eef4f3 100644 --- a/ethcore/src/db.rs +++ b/ethcore/src/db.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -218,15 +218,12 @@ impl Writable for DBTransaction { } impl Readable for KVDB { - fn read(&self, col: Option, key: &Key) -> Option where T: rlp::Decodable, R: Deref { - let result = self.get(col, &key.key()); + fn read(&self, col: Option, key: &Key) -> Option + where T: rlp::Decodable, R: Deref { + self.get(col, &key.key()) + .expect(&format!("db get failed, key: {:?}", &key.key() as &[u8])) + .map(|v| rlp::decode(&v).expect("decode db value failed") ) - match result { - Ok(option) => option.map(|v| rlp::decode(&v)), - Err(err) => { - panic!("db get failed, key: {:?}, err: {:?}", &key.key() as &[u8], err); - } - } } fn exists(&self, col: Option, key: &Key) -> bool where R: Deref { diff --git a/ethcore/src/encoded.rs b/ethcore/src/encoded.rs index 098a90a8ab467e19da3e51889741798106e77295..5bd723f0e244e42543563b034c5ac304e524a1d9 100644 --- a/ethcore/src/encoded.rs +++ b/ethcore/src/encoded.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -24,14 +24,13 @@ //! decoded object where parts like the hash can be saved. use block::Block as FullBlock; -use header::{BlockNumber, Header as FullHeader}; -use transaction::UnverifiedTransaction; -use views; - +use ethereum_types::{H256, Bloom, U256, Address}; use hash::keccak; +use header::{BlockNumber, Header as FullHeader}; use heapsize::HeapSizeOf; -use ethereum_types::{H256, Bloom, U256, Address}; -use rlp::Rlp; +use rlp::{self, Rlp, RlpStream}; +use transaction::UnverifiedTransaction; +use views::{self, BlockView, HeaderView, BodyView}; /// Owning header view. #[derive(Debug, Clone, PartialEq, Eq)] @@ -48,11 +47,13 @@ impl Header { pub fn new(encoded: Vec) -> Self { Header(encoded) } /// Upgrade this encoded view to a fully owned `Header` object. - pub fn decode(&self) -> FullHeader { ::rlp::decode(&self.0) } + pub fn decode(&self) -> Result { + rlp::decode(&self.0) + } /// Get a borrowed header view onto the data. #[inline] - pub fn view(&self) -> views::HeaderView { views::HeaderView::new(&self.0) } + pub fn view(&self) -> HeaderView { view!(HeaderView, &self.0) } /// Get the rlp of the header. #[inline] @@ -125,7 +126,7 @@ impl Body { /// Get a borrowed view of the data within. #[inline] - pub fn view(&self) -> views::BodyView { views::BodyView::new(&self.0) } + pub fn view(&self) -> BodyView { view!(BodyView, &self.0) } /// Fully decode this block body. pub fn decode(&self) -> (Vec, Vec) { @@ -144,6 +145,9 @@ impl Body { // forwarders to borrowed view. impl Body { + /// Get raw rlp of transactions + pub fn transactions_rlp(&self) -> Rlp { self.view().transactions_rlp().rlp } + /// Get a vector of all transactions. pub fn transactions(&self) -> Vec { self.view().transactions() } @@ -156,6 +160,9 @@ impl Body { /// The hash of each transaction in the block. pub fn transaction_hashes(&self) -> Vec { self.view().transaction_hashes() } + /// Get raw rlp of uncle headers + pub fn uncles_rlp(&self) -> Rlp { self.view().uncles_rlp().rlp } + /// Decode uncle headers. pub fn uncles(&self) -> Vec { self.view().uncles() } @@ -181,22 +188,31 @@ impl Block { /// Create a new owning block view. The raw bytes passed in must be an rlp-encoded block. pub fn new(raw: Vec) -> Self { Block(raw) } + /// Create a new owning block view by concatenating the encoded header and body + pub fn new_from_header_and_body(header: &views::HeaderView, body: &views::BodyView) -> Self { + let mut stream = RlpStream::new_list(3); + stream.append_raw(header.rlp().as_raw(), 1); + stream.append_raw(body.transactions_rlp().as_raw(), 1); + stream.append_raw(body.uncles_rlp().as_raw(), 1); + Block::new(stream.out()) + } + /// Get a borrowed view of the whole block. #[inline] - pub fn view(&self) -> views::BlockView { views::BlockView::new(&self.0) } + pub fn view(&self) -> BlockView { view!(BlockView, &self.0) } /// Get a borrowed view of the block header. #[inline] - pub fn header_view(&self) -> views::HeaderView { self.view().header_view() } + pub fn header_view(&self) -> HeaderView { self.view().header_view() } /// Decode to a full block. - pub fn decode(&self) -> FullBlock { ::rlp::decode(&self.0) } + pub fn decode(&self) -> Result { rlp::decode(&self.0) } /// Decode the header. - pub fn decode_header(&self) -> FullHeader { self.rlp().val_at(0) } + pub fn decode_header(&self) -> FullHeader { self.view().rlp().val_at(0) } /// Clone the encoded header. - pub fn header(&self) -> Header { Header(self.rlp().at(0).as_raw().to_vec()) } + pub fn header(&self) -> Header { Header(self.view().rlp().at(0).as_raw().to_vec()) } /// Get the rlp of this block. #[inline] diff --git a/ethcore/src/engines/authority_round/finality.rs b/ethcore/src/engines/authority_round/finality.rs index 61f1c182294d4e36f411d976657691df10ea3011..3745cde96c2f330a48040d1e393966dc75b8bf28 100644 --- a/ethcore/src/engines/authority_round/finality.rs +++ b/ethcore/src/engines/authority_round/finality.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/src/engines/authority_round/mod.rs b/ethcore/src/engines/authority_round/mod.rs index 0acde347e2b8aa848288a63999540ed27fce7da2..cc5c6e0b1cb6dd772120b84225cecf2a3089e46c 100644 --- a/ethcore/src/engines/authority_round/mod.rs +++ b/ethcore/src/engines/authority_round/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -19,7 +19,7 @@ use std::fmt; use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering as AtomicOrdering}; use std::sync::{Weak, Arc}; -use std::time::{UNIX_EPOCH, Duration}; +use std::time::{UNIX_EPOCH, SystemTime, Duration}; use std::collections::{BTreeMap, HashSet}; use std::iter::FromIterator; @@ -27,21 +27,23 @@ use account_provider::AccountProvider; use block::*; use client::EngineClient; use engines::{Engine, Seal, EngineError, ConstructedVerifier}; +use engines::block_reward; +use engines::block_reward::{BlockRewardContract, RewardKind}; use error::{Error, BlockError}; use ethjson; use machine::{AuxiliaryData, Call, EthereumMachine}; use hash::keccak; -use header::{Header, BlockNumber}; +use header::{Header, BlockNumber, ExtendedHeader}; use super::signer::EngineSigner; use super::validator_set::{ValidatorSet, SimpleList, new_validator_set}; use self::finality::RollingFinality; -use ethkey::{public_to_address, recover, verify_address, Signature}; +use ethkey::{self, Signature}; use io::{IoContext, IoHandler, TimerToken, IoService}; use itertools::{self, Itertools}; -use rlp::{encode, Decodable, DecoderError, Encodable, RlpStream, UntrustedRlp}; +use rlp::{encode, Decodable, DecoderError, Encodable, RlpStream, Rlp}; use ethereum_types::{H256, H520, Address, U128, U256}; use parking_lot::{Mutex, RwLock}; use unexpected::{Mismatch, OutOfBounds}; @@ -68,6 +70,10 @@ pub struct AuthorityRoundParams { pub immediate_transitions: bool, /// Block reward in base units. pub block_reward: U256, + /// Block reward contract transition block. + pub block_reward_contract_transition: u64, + /// Block reward contract. + pub block_reward_contract: Option, /// Number of accepted uncles transition block. pub maximum_uncle_count_transition: u64, /// Number of accepted uncles. @@ -95,6 +101,8 @@ impl From for AuthorityRoundParams { validate_step_transition: p.validate_step_transition.map_or(0, Into::into), immediate_transitions: p.immediate_transitions.unwrap_or(false), block_reward: p.block_reward.map_or_else(Default::default, Into::into), + block_reward_contract_transition: p.block_reward_contract_transition.map_or(0, Into::into), + block_reward_contract: p.block_reward_contract_address.map(BlockRewardContract::new), maximum_uncle_count_transition: p.maximum_uncle_count_transition.map_or(0, Into::into), maximum_uncle_count: p.maximum_uncle_count.map_or(0, Into::into), empty_steps_transition: p.empty_steps_transition.map_or(u64::max_value(), |n| ::std::cmp::max(n.into(), 1)), @@ -292,14 +300,14 @@ impl EmptyStep { let message = keccak(empty_step_rlp(self.step, &self.parent_hash)); let correct_proposer = step_proposer(validators, &self.parent_hash, self.step); - verify_address(&correct_proposer, &self.signature.into(), &message) + ethkey::verify_address(&correct_proposer, &self.signature.into(), &message) .map_err(|e| e.into()) } fn author(&self) -> Result { let message = keccak(empty_step_rlp(self.step, &self.parent_hash)); - let public = recover(&self.signature.into(), &message)?; - Ok(public_to_address(&public)) + let public = ethkey::recover(&self.signature.into(), &message)?; + Ok(ethkey::public_to_address(&public)) } fn sealed(&self) -> SealedEmptyStep { @@ -325,7 +333,7 @@ impl Encodable for EmptyStep { } impl Decodable for EmptyStep { - fn decode(rlp: &UntrustedRlp) -> Result { + fn decode(rlp: &Rlp) -> Result { let signature = rlp.val_at(0)?; let empty_step_rlp = rlp.at(1)?; @@ -366,7 +374,7 @@ impl Encodable for SealedEmptyStep { } impl Decodable for SealedEmptyStep { - fn decode(rlp: &UntrustedRlp) -> Result { + fn decode(rlp: &Rlp) -> Result { let signature = rlp.val_at(0)?; let step = rlp.val_at(1)?; @@ -374,12 +382,16 @@ impl Decodable for SealedEmptyStep { } } +struct PermissionedStep { + inner: Step, + can_propose: AtomicBool, +} + /// Engine using `AuthorityRound` proof-of-authority BFT consensus. pub struct AuthorityRound { transition_service: IoService<()>, - step: Arc, - can_propose: AtomicBool, - client: RwLock>>, + step: Arc, + client: Arc>>>, signer: RwLock, validators: Box, validate_score_transition: u64, @@ -388,6 +400,8 @@ pub struct AuthorityRound { epoch_manager: Mutex, immediate_transitions: bool, block_reward: U256, + block_reward_contract_transition: u64, + block_reward_contract: Option, maximum_uncle_count_transition: u64, maximum_uncle_count: usize, empty_steps_transition: u64, @@ -397,7 +411,7 @@ pub struct AuthorityRound { // header-chain validator. struct EpochVerifier { - step: Arc, + step: Arc, subchain_validators: SimpleList, empty_steps_transition: u64, } @@ -405,7 +419,7 @@ struct EpochVerifier { impl super::EpochVerifier for EpochVerifier { fn verify_light(&self, header: &Header) -> Result<(), Error> { // Validate the timestamp - verify_timestamp(&*self.step, header_step(header, self.empty_steps_transition)?)?; + verify_timestamp(&self.step.inner, header_step(header, self.empty_steps_transition)?)?; // always check the seal since it's fast. // nothing heavier to do. verify_external(header, &self.subchain_validators, self.empty_steps_transition) @@ -415,7 +429,7 @@ impl super::EpochVerifier for EpochVerifier { let mut finality_checker = RollingFinality::blank(self.subchain_validators.clone().into_inner()); let mut finalized = Vec::new(); - let headers: Vec
= UntrustedRlp::new(proof).as_list().ok()?; + let headers: Vec
= Rlp::new(proof).as_list().ok()?; { let mut push_header = |parent_header: &Header, header: Option<&Header>| { @@ -479,13 +493,13 @@ fn header_expected_seal_fields(header: &Header, empty_steps_transition: u64) -> fn header_step(header: &Header, empty_steps_transition: u64) -> Result { let expected_seal_fields = header_expected_seal_fields(header, empty_steps_transition); - UntrustedRlp::new(&header.seal().get(0).expect( + Rlp::new(&header.seal().get(0).expect( &format!("was either checked with verify_block_basic or is genesis; has {} fields; qed (Make sure the spec file has a correct genesis seal)", expected_seal_fields))).as_val() } fn header_signature(header: &Header, empty_steps_transition: u64) -> Result { let expected_seal_fields = header_expected_seal_fields(header, empty_steps_transition); - UntrustedRlp::new(&header.seal().get(1).expect( + Rlp::new(&header.seal().get(1).expect( &format!("was checked with verify_block_basic; has {} fields; qed", expected_seal_fields))).as_val::().map(Into::into) } @@ -498,7 +512,7 @@ fn header_empty_steps_raw(header: &Header) -> &[u8] { // extracts the empty steps from the header seal. should only be called when there are 3 fields in the seal // (i.e. header.number() >= self.empty_steps_transition). fn header_empty_steps(header: &Header) -> Result, ::rlp::DecoderError> { - let empty_steps = UntrustedRlp::new(header_empty_steps_raw(header)).as_list::()?; + let empty_steps = Rlp::new(header_empty_steps_raw(header)).as_list::()?; Ok(empty_steps.into_iter().map(|s| EmptyStep::from_sealed(s, header.parent_hash())).collect()) } @@ -536,6 +550,7 @@ fn verify_timestamp(step: &Step, header_step: usize) -> Result<(), BlockError> { // NOTE This error might be returned only in early stage of verification (Stage 1). // Returning it further won't recover the sync process. trace!(target: "engine", "verify_timestamp: block too early"); + let oob = oob.map(|n| SystemTime::now() + Duration::from_secs(n)); Err(BlockError::TemporarilyInvalid(oob).into()) }, Ok(_) => Ok(()), @@ -555,7 +570,7 @@ fn verify_external(header: &Header, validators: &ValidatorSet, empty_steps_trans }; let header_seal_hash = header_seal_hash(header, empty_steps_rlp); - !verify_address(&correct_proposer, &proposer_signature, &header_seal_hash)? + !ethkey::verify_address(&correct_proposer, &proposer_signature, &header_seal_hash)? }; if is_invalid_proposer { @@ -574,7 +589,7 @@ fn combine_proofs(signal_number: BlockNumber, set_proof: &[u8], finality_proof: } fn destructure_proofs(combined: &[u8]) -> Result<(BlockNumber, &[u8], &[u8]), Error> { - let rlp = UntrustedRlp::new(combined); + let rlp = Rlp::new(combined); Ok(( rlp.at(0)?.as_val()?, rlp.at(1)?.data()?, @@ -604,13 +619,15 @@ impl AuthorityRound { let engine = Arc::new( AuthorityRound { transition_service: IoService::<()>::start()?, - step: Arc::new(Step { - inner: AtomicUsize::new(initial_step), - calibrate: our_params.start_step.is_none(), - duration: our_params.step_duration, + step: Arc::new(PermissionedStep { + inner: Step { + inner: AtomicUsize::new(initial_step), + calibrate: our_params.start_step.is_none(), + duration: our_params.step_duration, + }, + can_propose: AtomicBool::new(true), }), - can_propose: AtomicBool::new(true), - client: RwLock::new(None), + client: Arc::new(RwLock::new(None)), signer: Default::default(), validators: our_params.validators, validate_score_transition: our_params.validate_score_transition, @@ -619,6 +636,8 @@ impl AuthorityRound { epoch_manager: Mutex::new(EpochManager::blank()), immediate_transitions: our_params.immediate_transitions, block_reward: our_params.block_reward, + block_reward_contract_transition: our_params.block_reward_contract_transition, + block_reward_contract: our_params.block_reward_contract, maximum_uncle_count_transition: our_params.maximum_uncle_count_transition, maximum_uncle_count: our_params.maximum_uncle_count, empty_steps_transition: our_params.empty_steps_transition, @@ -628,7 +647,10 @@ impl AuthorityRound { // Do not initialize timeouts for tests. if should_timeout { - let handler = TransitionHandler { engine: Arc::downgrade(&engine) }; + let handler = TransitionHandler { + step: engine.step.clone(), + client: engine.client.clone(), + }; engine.transition_service.register_handler(Arc::new(handler))?; } Ok(engine) @@ -642,7 +664,6 @@ impl AuthorityRound { }).cloned().collect() } - fn clear_empty_steps(&self, step: U256) { // clear old `empty_steps` messages self.empty_steps.lock().retain(|e| U256::from(e.step) > step); @@ -654,7 +675,7 @@ impl AuthorityRound { } fn generate_empty_step(&self, parent_hash: &H256) { - let step = self.step.load(); + let step = self.step.inner.load(); let empty_step_rlp = empty_step_rlp(step, parent_hash); if let Ok(signature) = self.sign(keccak(&empty_step_rlp)).map(Into::into) { @@ -686,34 +707,37 @@ fn unix_now() -> Duration { } struct TransitionHandler { - engine: Weak, + step: Arc, + client: Arc>>>, } const ENGINE_TIMEOUT_TOKEN: TimerToken = 23; impl IoHandler<()> for TransitionHandler { fn initialize(&self, io: &IoContext<()>) { - if let Some(engine) = self.engine.upgrade() { - let remaining = engine.step.duration_remaining(); - io.register_timer_once(ENGINE_TIMEOUT_TOKEN, remaining.as_millis()) - .unwrap_or_else(|e| warn!(target: "engine", "Failed to start consensus step timer: {}.", e)) - } + let remaining = AsMillis::as_millis(&self.step.inner.duration_remaining()); + io.register_timer_once(ENGINE_TIMEOUT_TOKEN, Duration::from_millis(remaining)) + .unwrap_or_else(|e| warn!(target: "engine", "Failed to start consensus step timer: {}.", e)) } fn timeout(&self, io: &IoContext<()>, timer: TimerToken) { if timer == ENGINE_TIMEOUT_TOKEN { - if let Some(engine) = self.engine.upgrade() { - // NOTE we might be lagging by couple of steps in case the timeout - // has not been called fast enough. - // Make sure to advance up to the actual step. - while engine.step.duration_remaining().as_millis() == 0 { - engine.step(); + // NOTE we might be lagging by couple of steps in case the timeout + // has not been called fast enough. + // Make sure to advance up to the actual step. + while AsMillis::as_millis(&self.step.inner.duration_remaining()) == 0 { + self.step.inner.increment(); + self.step.can_propose.store(true, AtomicOrdering::SeqCst); + if let Some(ref weak) = *self.client.read() { + if let Some(c) = weak.upgrade() { + c.update_sealing(); + } } - - let next_run_at = engine.step.duration_remaining().as_millis() >> 2; - io.register_timer_once(ENGINE_TIMEOUT_TOKEN, next_run_at) - .unwrap_or_else(|e| warn!(target: "engine", "Failed to restart consensus step timer: {}.", e)) } + + let next_run_at = AsMillis::as_millis(&self.step.inner.duration_remaining()) >> 2; + io.register_timer_once(ENGINE_TIMEOUT_TOKEN, Duration::from_millis(next_run_at)) + .unwrap_or_else(|e| warn!(target: "engine", "Failed to restart consensus step timer: {}.", e)) } } } @@ -730,8 +754,8 @@ impl Engine for AuthorityRound { } fn step(&self) { - self.step.increment(); - self.can_propose.store(true, AtomicOrdering::SeqCst); + self.step.inner.increment(); + self.step.can_propose.store(true, AtomicOrdering::SeqCst); if let Some(ref weak) = *self.client.read() { if let Some(c) = weak.upgrade() { c.update_sealing(); @@ -778,7 +802,7 @@ impl Engine for AuthorityRound { fn populate_from_parent(&self, header: &mut Header, parent: &Header) { let parent_step = header_step(parent, self.empty_steps_transition).expect("Header has been verified; qed"); - let current_step = self.step.load(); + let current_step = self.step.inner.load(); let current_empty_steps_len = if header.number() >= self.empty_steps_transition { self.empty_steps(parent_step.into(), current_step.into(), parent.hash()).len() @@ -800,11 +824,11 @@ impl Engine for AuthorityRound { EngineError::MalformedMessage(format!("{:?}", x)) } - let rlp = UntrustedRlp::new(rlp); + let rlp = Rlp::new(rlp); let empty_step: EmptyStep = rlp.as_val().map_err(fmt_err)?;; if empty_step.verify(&*self.validators).unwrap_or(false) { - if self.step.check_future(empty_step.step).is_ok() { + if self.step.inner.check_future(empty_step.step).is_ok() { trace!(target: "engine", "handle_message: received empty step message {:?}", empty_step); self.handle_empty_step_message(empty_step); } else { @@ -824,13 +848,16 @@ impl Engine for AuthorityRound { fn generate_seal(&self, block: &ExecutedBlock, parent: &Header) -> Seal { // first check to avoid generating signature most of the time // (but there's still a race to the `compare_and_swap`) - if !self.can_propose.load(AtomicOrdering::SeqCst) { return Seal::None; } + if !self.step.can_propose.load(AtomicOrdering::SeqCst) { + trace!(target: "engine", "Aborting seal generation. Can't propose."); + return Seal::None; + } let header = block.header(); let parent_step: U256 = header_step(parent, self.empty_steps_transition) .expect("Header has been verified; qed").into(); - let step = self.step.load(); + let step = self.step.inner.load(); // filter messages from old and future steps and different parents let empty_steps = if header.number() >= self.empty_steps_transition { @@ -907,7 +934,7 @@ impl Engine for AuthorityRound { trace!(target: "engine", "generate_seal: Issuing a block for step {}.", step); // only issue the seal if we were the first to reach the compare_and_swap. - if self.can_propose.compare_and_swap(true, false, AtomicOrdering::SeqCst) { + if self.step.can_propose.compare_and_swap(true, false, AtomicOrdering::SeqCst) { self.clear_empty_steps(parent_step); @@ -941,6 +968,7 @@ impl Engine for AuthorityRound { &self, block: &mut ExecutedBlock, epoch_begin: bool, + _ancestry: &mut Iterator, ) -> Result<(), Error> { // with immediate transitions, we don't use the epoch mechanism anyway. // the genesis is always considered an epoch, but we ignore it intentionally. @@ -966,9 +994,7 @@ impl Engine for AuthorityRound { /// Apply the block reward on finalisation of the block. fn on_close_block(&self, block: &mut ExecutedBlock) -> Result<(), Error> { - use parity_machine::WithBalances; - - let mut rewards = Vec::new(); + let mut benefactors = Vec::new(); if block.header().number() >= self.empty_steps_transition { let empty_steps = if block.header().seal().is_empty() { // this is a new block, calculate rewards based on the empty steps messages we have accumulated @@ -982,10 +1008,10 @@ impl Engine for AuthorityRound { let parent = client.block_header(::client::BlockId::Hash(*block.header().parent_hash())) .expect("hash is from parent; parent header must exist; qed") - .decode(); + .decode()?; let parent_step = header_step(&parent, self.empty_steps_transition)?; - let current_step = self.step.load(); + let current_step = self.step.inner.load(); self.empty_steps(parent_step.into(), current_step.into(), parent.hash()) } else { // we're verifying a block, extract empty steps from the seal @@ -994,17 +1020,36 @@ impl Engine for AuthorityRound { for empty_step in empty_steps { let author = empty_step.author()?; - rewards.push((author, self.block_reward)); + benefactors.push((author, RewardKind::EmptyStep)); } } let author = *block.header().author(); - rewards.push((author, self.block_reward)); + benefactors.push((author, RewardKind::Author)); + + let rewards: Vec<_> = match self.block_reward_contract { + Some(ref c) if block.header().number() >= self.block_reward_contract_transition => { + // NOTE: this logic should be moved to a function when another + // engine needs support for block reward contract. + let mut call = |to, data| { + let result = self.machine.execute_as_system( + block, + to, + U256::max_value(), // unbounded gas? maybe make configurable. + Some(data), + ); + result.map_err(|e| format!("{}", e)) + }; - for &(ref author, ref block_reward) in rewards.iter() { - self.machine.add_balance(block, author, block_reward)?; - } - self.machine.note_rewards(block, &rewards, &[]) + let rewards = c.reward(&benefactors, &mut call)?; + rewards.into_iter().map(|(author, amount)| (author, RewardKind::External, amount)).collect() + }, + _ => { + benefactors.into_iter().map(|(author, reward_kind)| (author, reward_kind, self.block_reward)).collect() + }, + }; + + block_reward::apply_block_rewards(&rewards, block, &self.machine) } /// Check the number of seal fields. @@ -1019,7 +1064,7 @@ impl Engine for AuthorityRound { // If yes then probably benign reporting needs to be moved further in the verification. let set_number = header.number(); - match verify_timestamp(&*self.step, header_step(header, self.empty_steps_transition)?) { + match verify_timestamp(&self.step.inner, header_step(header, self.empty_steps_transition)?) { Err(BlockError::InvalidSeal) => { self.validators.report_benign(header.author(), set_number, header.number()); Err(BlockError::InvalidSeal.into()) @@ -1261,7 +1306,7 @@ impl Engine for AuthorityRound { // This way, upon encountering an epoch change, the proposer from the // new set will be forced to wait until the next step to avoid sealing a // block that breaks the invariant that the parent's step < the block's step. - self.can_propose.store(false, AtomicOrdering::SeqCst); + self.step.can_propose.store(false, AtomicOrdering::SeqCst); return Some(combine_proofs(signal_number, &pending.proof, &*finality_proof)); } } @@ -1305,7 +1350,7 @@ impl Engine for AuthorityRound { } fn sign(&self, hash: H256) -> Result { - self.signer.read().sign(hash).map_err(Into::into) + Ok(self.signer.read().sign(hash)?) } fn snapshot_components(&self) -> Option> { @@ -1315,6 +1360,10 @@ impl Engine for AuthorityRound { Some(Box::new(::snapshot::PoaSnapshot)) } } + + fn fork_choice(&self, new: &ExtendedHeader, current: &ExtendedHeader) -> super::ForkChoice { + super::total_difficulty_fork_choice(new, current) + } } #[cfg(test)] @@ -1326,7 +1375,7 @@ mod tests { use header::Header; use rlp::encode; use block::*; - use tests::helpers::{ + use test_helpers::{ generate_dummy_client_with_spec_and_accounts, get_temp_state_db, generate_dummy_client, TestNotify }; @@ -1335,7 +1384,7 @@ mod tests { use transaction::{Action, Transaction}; use engines::{Seal, Engine, EngineError, EthEngine}; use engines::validator_set::TestSet; - use error::Error; + use error::{Error, ErrorKind}; use super::{AuthorityRoundParams, AuthorityRound, EmptyStep, SealedEmptyStep}; #[test] @@ -1374,9 +1423,9 @@ mod tests { let db1 = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); let db2 = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); let last_hashes = Arc::new(vec![genesis_header.hash()]); - let b1 = OpenBlock::new(engine, Default::default(), false, db1, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false).unwrap(); + let b1 = OpenBlock::new(engine, Default::default(), false, db1, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false, &mut Vec::new().into_iter()).unwrap(); let b1 = b1.close_and_lock(); - let b2 = OpenBlock::new(engine, Default::default(), false, db2, &genesis_header, last_hashes, addr2, (3141562.into(), 31415620.into()), vec![], false).unwrap(); + let b2 = OpenBlock::new(engine, Default::default(), false, db2, &genesis_header, last_hashes, addr2, (3141562.into(), 31415620.into()), vec![], false, &mut Vec::new().into_iter()).unwrap(); let b2 = b2.close_and_lock(); engine.set_signer(tap.clone(), addr1, "1".into()); @@ -1408,9 +1457,9 @@ mod tests { let db2 = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); let last_hashes = Arc::new(vec![genesis_header.hash()]); - let b1 = OpenBlock::new(engine, Default::default(), false, db1, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false).unwrap(); + let b1 = OpenBlock::new(engine, Default::default(), false, db1, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false, &mut Vec::new().into_iter()).unwrap(); let b1 = b1.close_and_lock(); - let b2 = OpenBlock::new(engine, Default::default(), false, db2, &genesis_header, last_hashes, addr2, (3141562.into(), 31415620.into()), vec![], false).unwrap(); + let b2 = OpenBlock::new(engine, Default::default(), false, db2, &genesis_header, last_hashes, addr2, (3141562.into(), 31415620.into()), vec![], false, &mut Vec::new().into_iter()).unwrap(); let b2 = b2.close_and_lock(); engine.set_signer(tap.clone(), addr1, "1".into()); @@ -1517,6 +1566,8 @@ mod tests { empty_steps_transition: u64::max_value(), maximum_empty_steps: 0, block_reward: Default::default(), + block_reward_contract_transition: 0, + block_reward_contract: Default::default(), }; let aura = { @@ -1559,6 +1610,8 @@ mod tests { empty_steps_transition: u64::max_value(), maximum_empty_steps: 0, block_reward: Default::default(), + block_reward_contract_transition: 0, + block_reward_contract: Default::default(), }; let aura = { @@ -1613,6 +1666,8 @@ mod tests { empty_steps_transition: u64::max_value(), maximum_empty_steps: 0, block_reward: Default::default(), + block_reward_contract_transition: 0, + block_reward_contract: Default::default(), }; let mut c_params = ::spec::CommonParams::default(); @@ -1657,7 +1712,7 @@ mod tests { let db1 = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); let last_hashes = Arc::new(vec![genesis_header.hash()]); - let b1 = OpenBlock::new(engine, Default::default(), false, db1, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false).unwrap(); + let b1 = OpenBlock::new(engine, Default::default(), false, db1, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false, &mut Vec::new().into_iter()).unwrap(); let b1 = b1.close_and_lock(); let client = generate_dummy_client(0); @@ -1692,7 +1747,7 @@ mod tests { let last_hashes = Arc::new(vec![genesis_header.hash()]); // step 2 - let b1 = OpenBlock::new(engine, Default::default(), false, db1, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false).unwrap(); + let b1 = OpenBlock::new(engine, Default::default(), false, db1, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false, &mut Vec::new().into_iter()).unwrap(); let b1 = b1.close_and_lock(); // since the block is empty it isn't sealed and we generate empty steps @@ -1701,7 +1756,7 @@ mod tests { engine.step(); // step 3 - let mut b2 = OpenBlock::new(engine, Default::default(), false, db2, &genesis_header, last_hashes.clone(), addr2, (3141562.into(), 31415620.into()), vec![], false).unwrap(); + let mut b2 = OpenBlock::new(engine, Default::default(), false, db2, &genesis_header, last_hashes.clone(), addr2, (3141562.into(), 31415620.into()), vec![], false, &mut Vec::new().into_iter()).unwrap(); b2.push_transaction(Transaction { action: Action::Create, nonce: U256::from(0), @@ -1740,7 +1795,7 @@ mod tests { let last_hashes = Arc::new(vec![genesis_header.hash()]); // step 2 - let b1 = OpenBlock::new(engine, Default::default(), false, db1, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false).unwrap(); + let b1 = OpenBlock::new(engine, Default::default(), false, db1, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false, &mut Vec::new().into_iter()).unwrap(); let b1 = b1.close_and_lock(); // since the block is empty it isn't sealed and we generate empty steps @@ -1749,7 +1804,7 @@ mod tests { engine.step(); // step 3 - let b2 = OpenBlock::new(engine, Default::default(), false, db2, &genesis_header, last_hashes.clone(), addr2, (3141562.into(), 31415620.into()), vec![], false).unwrap(); + let b2 = OpenBlock::new(engine, Default::default(), false, db2, &genesis_header, last_hashes.clone(), addr2, (3141562.into(), 31415620.into()), vec![], false, &mut Vec::new().into_iter()).unwrap(); let b2 = b2.close_and_lock(); engine.set_signer(tap.clone(), addr2, "0".into()); assert_eq!(engine.generate_seal(b2.block(), &genesis_header), Seal::None); @@ -1757,7 +1812,7 @@ mod tests { // step 4 // the spec sets the maximum_empty_steps to 2 so we will now seal an empty block and include the empty step messages - let b3 = OpenBlock::new(engine, Default::default(), false, db3, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false).unwrap(); + let b3 = OpenBlock::new(engine, Default::default(), false, db3, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false, &mut Vec::new().into_iter()).unwrap(); let b3 = b3.close_and_lock(); engine.set_signer(tap.clone(), addr1, "1".into()); @@ -1790,7 +1845,7 @@ mod tests { engine.register_client(Arc::downgrade(&client) as _); // step 2 - let b1 = OpenBlock::new(engine, Default::default(), false, db1, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false).unwrap(); + let b1 = OpenBlock::new(engine, Default::default(), false, db1, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false, &mut Vec::new().into_iter()).unwrap(); let b1 = b1.close_and_lock(); // since the block is empty it isn't sealed and we generate empty steps @@ -1800,7 +1855,7 @@ mod tests { // step 3 // the signer of the accumulated empty step message should be rewarded - let b2 = OpenBlock::new(engine, Default::default(), false, db2, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false).unwrap(); + let b2 = OpenBlock::new(engine, Default::default(), false, db2, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false, &mut Vec::new().into_iter()).unwrap(); let addr1_balance = b2.block().state().balance(&addr1).unwrap(); // after closing the block `addr1` should be reward twice, one for the included empty step message and another for block creation @@ -1838,7 +1893,7 @@ mod tests { ]); assert!(match engine.verify_block_family(&header, &parent_header) { - Err(Error::Engine(EngineError::InsufficientProof(ref s))) + Err(Error(ErrorKind::Engine(EngineError::InsufficientProof(ref s)), _)) if s.contains("invalid step") => true, _ => false, }); @@ -1852,7 +1907,7 @@ mod tests { ]); assert!(match engine.verify_block_family(&header, &parent_header) { - Err(Error::Engine(EngineError::InsufficientProof(ref s))) + Err(Error(ErrorKind::Engine(EngineError::InsufficientProof(ref s)), _)) if s.contains("invalid empty step proof") => true, _ => false, }); @@ -1867,7 +1922,7 @@ mod tests { ]); assert!(match engine.verify_block_family(&header, &parent_header) { - Err(Error::Engine(EngineError::InsufficientProof(ref s))) + Err(Error(ErrorKind::Engine(EngineError::InsufficientProof(ref s)), _)) if s.contains("invalid empty step proof") => true, _ => false, }); @@ -1890,4 +1945,73 @@ mod tests { _ => false, }); } + + #[test] + fn block_reward_contract() { + let spec = Spec::new_test_round_block_reward_contract(); + let tap = Arc::new(AccountProvider::transient_provider()); + + let addr1 = tap.insert_account(keccak("1").into(), "1").unwrap(); + + let engine = &*spec.engine; + let genesis_header = spec.genesis_header(); + let db1 = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); + let db2 = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); + + let last_hashes = Arc::new(vec![genesis_header.hash()]); + + let client = generate_dummy_client_with_spec_and_accounts( + Spec::new_test_round_block_reward_contract, + None, + ); + engine.register_client(Arc::downgrade(&client) as _); + + // step 2 + let b1 = OpenBlock::new( + engine, + Default::default(), + false, + db1, + &genesis_header, + last_hashes.clone(), + addr1, + (3141562.into(), 31415620.into()), + vec![], + false, + &mut Vec::new().into_iter(), + ).unwrap(); + let b1 = b1.close_and_lock(); + + // since the block is empty it isn't sealed and we generate empty steps + engine.set_signer(tap.clone(), addr1, "1".into()); + assert_eq!(engine.generate_seal(b1.block(), &genesis_header), Seal::None); + engine.step(); + + // step 3 + // the signer of the accumulated empty step message should be rewarded + let b2 = OpenBlock::new( + engine, + Default::default(), + false, + db2, + &genesis_header, + last_hashes.clone(), + addr1, + (3141562.into(), 31415620.into()), + vec![], + false, + &mut Vec::new().into_iter(), + ).unwrap(); + let addr1_balance = b2.block().state().balance(&addr1).unwrap(); + + // after closing the block `addr1` should be reward twice, one for the included empty step + // message and another for block creation + let b2 = b2.close_and_lock(); + + // the contract rewards (1000 + kind) for each benefactor/reward kind + assert_eq!( + b2.block().state().balance(&addr1).unwrap(), + addr1_balance + (1000 + 0).into() + (1000 + 2).into(), + ) + } } diff --git a/ethcore/src/engines/basic_authority.rs b/ethcore/src/engines/basic_authority.rs index 4d1f38dce4cbb770e4302da6bc3b471e6c74ca67..dde0af2d9605ac4eec52e90aaac65b645749969b 100644 --- a/ethcore/src/engines/basic_authority.rs +++ b/ethcore/src/engines/basic_authority.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -19,13 +19,13 @@ use std::sync::{Weak, Arc}; use ethereum_types::{H256, H520, Address}; use parking_lot::RwLock; -use ethkey::{recover, public_to_address, Signature}; +use ethkey::{self, Signature}; use account_provider::AccountProvider; use block::*; use engines::{Engine, Seal, ConstructedVerifier, EngineError}; use error::{BlockError, Error}; use ethjson; -use header::Header; +use header::{Header, ExtendedHeader}; use client::EngineClient; use machine::{AuxiliaryData, Call, EthereumMachine}; use super::signer::EngineSigner; @@ -57,11 +57,11 @@ impl super::EpochVerifier for EpochVerifier { } fn verify_external(header: &Header, validators: &ValidatorSet) -> Result<(), Error> { - use rlp::UntrustedRlp; + use rlp::Rlp; // Check if the signature belongs to a validator, can depend on parent state. - let sig = UntrustedRlp::new(&header.seal()[0]).as_val::()?; - let signer = public_to_address(&recover(&sig.into(), &header.bare_hash())?); + let sig = Rlp::new(&header.seal()[0]).as_val::()?; + let signer = ethkey::public_to_address(ðkey::recover(&sig.into(), &header.bare_hash())?); if *header.author() != signer { return Err(EngineError::NotAuthorized(*header.author()).into()) @@ -185,12 +185,16 @@ impl Engine for BasicAuthority { } fn sign(&self, hash: H256) -> Result { - self.signer.read().sign(hash).map_err(Into::into) + Ok(self.signer.read().sign(hash)?) } fn snapshot_components(&self) -> Option> { None } + + fn fork_choice(&self, new: &ExtendedHeader, current: &ExtendedHeader) -> super::ForkChoice { + super::total_difficulty_fork_choice(new, current) + } } #[cfg(test)] @@ -199,7 +203,7 @@ mod tests { use hash::keccak; use ethereum_types::H520; use block::*; - use tests::helpers::get_temp_state_db; + use test_helpers::get_temp_state_db; use account_provider::AccountProvider; use header::Header; use spec::Spec; @@ -247,7 +251,7 @@ mod tests { let genesis_header = spec.genesis_header(); let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); let last_hashes = Arc::new(vec![genesis_header.hash()]); - let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, addr, (3141562.into(), 31415620.into()), vec![], false).unwrap(); + let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, addr, (3141562.into(), 31415620.into()), vec![], false, &mut Vec::new().into_iter()).unwrap(); let b = b.close_and_lock(); if let Seal::Regular(seal) = engine.generate_seal(b.block(), &genesis_header) { assert!(b.try_seal(engine, seal).is_ok()); diff --git a/ethcore/src/engines/block_reward.rs b/ethcore/src/engines/block_reward.rs new file mode 100644 index 0000000000000000000000000000000000000000..9a9d54e4af150318b22ae4082fe47bfb1d8044fd --- /dev/null +++ b/ethcore/src/engines/block_reward.rs @@ -0,0 +1,204 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! A module with types for declaring block rewards and a client interface for interacting with a +//! block reward contract. + +use ethabi; +use ethabi::ParamType; +use ethereum_types::{H160, Address, U256}; + +use error::Error; +use machine::WithRewards; +use parity_machine::{Machine, WithBalances}; +use trace; +use super::SystemCall; + +use_contract!(block_reward_contract, "BlockReward", "res/contracts/block_reward.json"); + +/// The kind of block reward. +/// Depending on the consensus engine the allocated block reward might have +/// different semantics which could lead e.g. to different reward values. +#[repr(u8)] +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub enum RewardKind { + /// Reward attributed to the block author. + Author = 0, + /// Reward attributed to the block uncle(s). + Uncle = 1, + /// Reward attributed to the author(s) of empty step(s) included in the block (AuthorityRound engine). + EmptyStep = 2, + /// Reward attributed by an external protocol (e.g. block reward contract). + External = 3, +} + +impl From for u16 { + fn from(reward_kind: RewardKind) -> Self { + reward_kind as u16 + } +} + +impl Into for RewardKind { + fn into(self) -> trace::RewardType { + match self { + RewardKind::Author => trace::RewardType::Block, + RewardKind::Uncle => trace::RewardType::Uncle, + RewardKind::EmptyStep => trace::RewardType::EmptyStep, + RewardKind::External => trace::RewardType::External, + } + } +} + +/// A client for the block reward contract. +pub struct BlockRewardContract { + /// Address of the contract. + address: Address, + block_reward_contract: block_reward_contract::BlockReward, +} + +impl BlockRewardContract { + /// Create a new block reward contract client targeting the given address. + pub fn new(address: Address) -> BlockRewardContract { + BlockRewardContract { + address, + block_reward_contract: block_reward_contract::BlockReward::default(), + } + } + + /// Calls the block reward contract with the given benefactors list (and associated reward kind) + /// and returns the reward allocation (address - value). The block reward contract *must* be + /// called by the system address so the `caller` must ensure that (e.g. using + /// `machine.execute_as_system`). + pub fn reward( + &self, + benefactors: &[(Address, RewardKind)], + caller: &mut SystemCall, + ) -> Result, Error> { + let reward = self.block_reward_contract.functions().reward(); + + let input = reward.input( + benefactors.iter().map(|&(address, _)| H160::from(address)), + benefactors.iter().map(|&(_, ref reward_kind)| u16::from(*reward_kind)), + ); + + let output = caller(self.address, input) + .map_err(Into::into) + .map_err(::engines::EngineError::FailedSystemCall)?; + + // since this is a non-constant call we can't use ethabi's function output + // deserialization, sadness ensues. + let types = &[ + ParamType::Array(Box::new(ParamType::Address)), + ParamType::Array(Box::new(ParamType::Uint(256))), + ]; + + let tokens = ethabi::decode(types, &output) + .map_err(|err| err.to_string()) + .map_err(::engines::EngineError::FailedSystemCall)?; + + assert!(tokens.len() == 2); + + let addresses = tokens[0].clone().to_array().expect("type checked by ethabi::decode; qed"); + let rewards = tokens[1].clone().to_array().expect("type checked by ethabi::decode; qed"); + + if addresses.len() != rewards.len() { + return Err(::engines::EngineError::FailedSystemCall( + "invalid data returned by reward contract: both arrays must have the same size".into() + ).into()); + } + + let addresses = addresses.into_iter().map(|t| t.to_address().expect("type checked by ethabi::decode; qed")); + let rewards = rewards.into_iter().map(|t| t.to_uint().expect("type checked by ethabi::decode; qed")); + + Ok(addresses.zip(rewards).collect()) + } +} + +/// Applies the given block rewards, i.e. adds the given balance to each benefactors' address. +/// If tracing is enabled the operations are recorded. +pub fn apply_block_rewards( + rewards: &[(Address, RewardKind, U256)], + block: &mut M::LiveBlock, + machine: &M, +) -> Result<(), M::Error> { + for &(ref author, _, ref block_reward) in rewards { + machine.add_balance(block, author, block_reward)?; + } + + let rewards: Vec<_> = rewards.into_iter().map(|&(a, k, r)| (a, k.into(), r)).collect(); + machine.note_rewards(block, &rewards) +} + +#[cfg(test)] +mod test { + use client::PrepareOpenBlock; + use ethereum_types::U256; + use spec::Spec; + use test_helpers::generate_dummy_client_with_spec_and_accounts; + + use super::{BlockRewardContract, RewardKind}; + + #[test] + fn block_reward_contract() { + let client = generate_dummy_client_with_spec_and_accounts( + Spec::new_test_round_block_reward_contract, + None, + ); + + let machine = Spec::new_test_machine(); + + // the spec has a block reward contract defined at the given address + let block_reward_contract = BlockRewardContract::new( + "0000000000000000000000000000000000000042".into(), + ); + + let mut call = |to, data| { + let mut block = client.prepare_open_block( + "0000000000000000000000000000000000000001".into(), + (3141562.into(), 31415620.into()), + vec![], + ); + + let result = machine.execute_as_system( + block.block_mut(), + to, + U256::max_value(), + Some(data), + ); + + result.map_err(|e| format!("{}", e)) + }; + + // if no benefactors are given no rewards are attributed + assert!(block_reward_contract.reward(&vec![], &mut call).unwrap().is_empty()); + + // the contract rewards (1000 + kind) for each benefactor + let benefactors = vec![ + ("0000000000000000000000000000000000000033".into(), RewardKind::Author), + ("0000000000000000000000000000000000000034".into(), RewardKind::Uncle), + ("0000000000000000000000000000000000000035".into(), RewardKind::EmptyStep), + ]; + + let rewards = block_reward_contract.reward(&benefactors, &mut call).unwrap(); + let expected = vec![ + ("0000000000000000000000000000000000000033".into(), U256::from(1000)), + ("0000000000000000000000000000000000000034".into(), U256::from(1000 + 1)), + ("0000000000000000000000000000000000000035".into(), U256::from(1000 + 2)), + ]; + + assert_eq!(expected, rewards); + } +} diff --git a/ethcore/src/engines/epoch.rs b/ethcore/src/engines/epoch.rs index dffc822cbf819dfbe4d6cf05953d911b63741a09..53b540cabd1cec8bbdf6d3b47192d2db3565e1a7 100644 --- a/ethcore/src/engines/epoch.rs +++ b/ethcore/src/engines/epoch.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -18,7 +18,7 @@ use ethereum_types::H256; -use rlp::{Encodable, Decodable, DecoderError, RlpStream, UntrustedRlp}; +use rlp::{Encodable, Decodable, DecoderError, RlpStream, Rlp}; /// A full epoch transition. #[derive(Debug, Clone)] @@ -41,7 +41,7 @@ impl Encodable for Transition { } impl Decodable for Transition { - fn decode(rlp: &UntrustedRlp) -> Result { + fn decode(rlp: &Rlp) -> Result { Ok(Transition { block_hash: rlp.val_at(0)?, block_number: rlp.val_at(1)?, @@ -64,7 +64,7 @@ impl Encodable for PendingTransition { } impl Decodable for PendingTransition { - fn decode(rlp: &UntrustedRlp) -> Result { + fn decode(rlp: &Rlp) -> Result { Ok(PendingTransition { proof: rlp.as_val()?, }) diff --git a/ethcore/src/engines/instant_seal.rs b/ethcore/src/engines/instant_seal.rs index 9c2600212d09c6ed9b4f7f6ac6dc982c11617f0e..a35dea5219a2e37640cc853a129ffb1fe752de9e 100644 --- a/ethcore/src/engines/instant_seal.rs +++ b/ethcore/src/engines/instant_seal.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -15,7 +15,7 @@ // along with Parity. If not, see . use engines::{Engine, Seal}; -use parity_machine::{Machine, Transactions}; +use parity_machine::{Machine, Transactions, TotalScoredHeader}; /// An engine which does not provide any consensus mechanism, just seals blocks internally. /// Only seals blocks which have transactions. @@ -33,7 +33,9 @@ impl InstantSeal { } impl Engine for InstantSeal - where M::LiveBlock: Transactions + where M::LiveBlock: Transactions, + M::ExtendedHeader: TotalScoredHeader, + ::Value: Ord { fn name(&self) -> &str { "InstantSeal" @@ -50,13 +52,28 @@ impl Engine for InstantSeal fn verify_local_seal(&self, _header: &M::Header) -> Result<(), M::Error> { Ok(()) } + + fn open_block_header_timestamp(&self, parent_timestamp: u64) -> u64 { + use std::{time, cmp}; + + let now = time::SystemTime::now().duration_since(time::UNIX_EPOCH).unwrap_or_default(); + cmp::max(now.as_secs(), parent_timestamp) + } + + fn is_timestamp_valid(&self, header_timestamp: u64, parent_timestamp: u64) -> bool { + header_timestamp >= parent_timestamp + } + + fn fork_choice(&self, new: &M::ExtendedHeader, current: &M::ExtendedHeader) -> super::ForkChoice { + super::total_difficulty_fork_choice(new, current) + } } #[cfg(test)] mod tests { use std::sync::Arc; use ethereum_types::{H520, Address}; - use tests::helpers::{get_temp_state_db}; + use test_helpers::get_temp_state_db; use spec::Spec; use header::Header; use block::*; @@ -69,7 +86,7 @@ mod tests { let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); let genesis_header = spec.genesis_header(); let last_hashes = Arc::new(vec![genesis_header.hash()]); - let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::default(), (3141562.into(), 31415620.into()), vec![], false).unwrap(); + let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::default(), (3141562.into(), 31415620.into()), vec![], false, &mut Vec::new().into_iter()).unwrap(); let b = b.close_and_lock(); if let Seal::Regular(seal) = engine.generate_seal(b.block(), &genesis_header) { assert!(b.try_seal(engine, seal).is_ok()); diff --git a/ethcore/src/engines/mod.rs b/ethcore/src/engines/mod.rs index 5b2b9abb0e06c7337aaa8c2effadce73003571e6..54a9dde2e64e5f655cc89eb2e5c49b11cef230e1 100644 --- a/ethcore/src/engines/mod.rs +++ b/ethcore/src/engines/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -26,6 +26,7 @@ mod transition; mod validator_set; mod vote_collector; +pub mod block_reward; pub mod epoch; pub use self::authority_round::AuthorityRound; @@ -37,7 +38,7 @@ pub use self::tendermint::Tendermint; use std::sync::{Weak, Arc}; use std::collections::{BTreeMap, HashMap}; -use std::fmt; +use std::{fmt, error}; use self::epoch::PendingTransition; @@ -48,18 +49,28 @@ use error::Error; use header::{Header, BlockNumber}; use snapshot::SnapshotComponents; use spec::CommonParams; -use transaction::{UnverifiedTransaction, SignedTransaction}; +use transaction::{self, UnverifiedTransaction, SignedTransaction}; use ethkey::Signature; -use parity_machine::{Machine, LocalizedMachine as Localized}; +use parity_machine::{Machine, LocalizedMachine as Localized, TotalScoredHeader}; use ethereum_types::{H256, U256, Address}; use unexpected::{Mismatch, OutOfBounds}; use bytes::Bytes; +use types::ancestry_action::AncestryAction; -/// Default EIP-210 contrat code. +/// Default EIP-210 contract code. /// As defined in https://github.com/ethereum/EIPs/pull/210 pub const DEFAULT_BLOCKHASH_CONTRACT: &'static str = "73fffffffffffffffffffffffffffffffffffffffe33141561006a5760014303600035610100820755610100810715156100455760003561010061010083050761010001555b6201000081071515610064576000356101006201000083050761020001555b5061013e565b4360003512151561008457600060405260206040f361013d565b61010060003543031315156100a857610100600035075460605260206060f361013c565b6101006000350715156100c55762010000600035430313156100c8565b60005b156100ea576101006101006000350507610100015460805260206080f361013b565b620100006000350715156101095763010000006000354303131561010c565b60005b1561012f57610100620100006000350507610200015460a052602060a0f361013a565b600060c052602060c0f35b5b5b5b5b"; +/// Fork choice. +#[derive(Debug, PartialEq, Eq)] +pub enum ForkChoice { + /// Choose the new block. + New, + /// Choose the current best block. + Old, +} + /// Voting errors. #[derive(Debug)] pub enum EngineError { @@ -102,6 +113,12 @@ impl fmt::Display for EngineError { } } +impl error::Error for EngineError { + fn description(&self) -> &str { + "Engine error" + } +} + /// Seal type. #[derive(Debug, PartialEq, Eq)] pub enum Seal { @@ -113,6 +130,9 @@ pub enum Seal { None, } +/// A system-calling closure. Enacts calls on a block's state from the system address. +pub type SystemCall<'a> = FnMut(Address, Vec) -> Result, String> + 'a; + /// Type alias for a function we can get headers by hash through. pub type Headers<'a, H> = Fn(H256) -> Option + 'a; @@ -198,6 +218,7 @@ pub trait Engine: Sync + Send { &self, _block: &mut M::LiveBlock, _epoch_begin: bool, + _ancestry: &mut Iterator, ) -> Result<(), M::Error> { Ok(()) } @@ -242,11 +263,11 @@ pub trait Engine: Sync + Send { fn verify_block_unordered(&self, _header: &M::Header) -> Result<(), M::Error> { Ok(()) } /// Phase 3 verification. Check block information against parent. Returns either a null `Ok` or a general error detailing the problem with import. - fn verify_block_family(&self, _header: &M::Header, _parent: &M::Header) -> Result<(), Error> { Ok(()) } + fn verify_block_family(&self, _header: &M::Header, _parent: &M::Header) -> Result<(), M::Error> { Ok(()) } /// Phase 4 verification. Verify block header against potentially external data. /// Should only be called when `register_client` has been called previously. - fn verify_block_external(&self, _header: &M::Header) -> Result<(), Error> { Ok(()) } + fn verify_block_external(&self, _header: &M::Header) -> Result<(), M::Error> { Ok(()) } /// Genesis epoch data. fn genesis_epoch_data<'a>(&self, _header: &M::Header, _state: &>::StateContext) -> Result, String> { Ok(Vec::new()) } @@ -304,7 +325,7 @@ pub trait Engine: Sync + Send { fn set_signer(&self, _account_provider: Arc, _address: Address, _password: String) {} /// Sign using the EngineSigner, to be used for consensus tx signing. - fn sign(&self, _hash: H256) -> Result { unimplemented!() } + fn sign(&self, _hash: H256) -> Result { unimplemented!() } /// Add Client which can be used for sealing, potentially querying the state and sending messages. fn register_client(&self, _client: Weak) {} @@ -325,6 +346,37 @@ pub trait Engine: Sync + Send { fn supports_warp(&self) -> bool { self.snapshot_components().is_some() } + + /// Return a new open block header timestamp based on the parent timestamp. + fn open_block_header_timestamp(&self, parent_timestamp: u64) -> u64 { + use std::{time, cmp}; + + let now = time::SystemTime::now().duration_since(time::UNIX_EPOCH).unwrap_or_default(); + cmp::max(now.as_secs() as u64, parent_timestamp + 1) + } + + /// Check whether the parent timestamp is valid. + fn is_timestamp_valid(&self, header_timestamp: u64, parent_timestamp: u64) -> bool { + header_timestamp > parent_timestamp + } + + /// Gather all ancestry actions. Called at the last stage when a block is committed. The Engine must guarantee that + /// the ancestry exists. + fn ancestry_actions(&self, _block: &M::LiveBlock, _ancestry: &mut Iterator) -> Vec { + Vec::new() + } + + /// Check whether the given new block is the best block, after finalization check. + fn fork_choice(&self, new: &M::ExtendedHeader, best: &M::ExtendedHeader) -> ForkChoice; +} + +/// Check whether a given block is the best block based on the default total difficulty rule. +pub fn total_difficulty_fork_choice(new: &T, best: &T) -> ForkChoice where ::Value: Ord { + if new.total_score() > best.total_score() { + ForkChoice::New + } else { + ForkChoice::Old + } } /// Common type alias for an engine coupled with an Ethereum-like state machine. @@ -374,14 +426,28 @@ pub trait EthEngine: Engine<::machine::EthereumMachine> { } /// Verify a particular transaction is valid. - fn verify_transaction_unordered(&self, t: UnverifiedTransaction, header: &Header) -> Result { + /// + /// Unordered verification doesn't rely on the transaction execution order, + /// i.e. it should only verify stuff that doesn't assume any previous transactions + /// has already been verified and executed. + /// + /// NOTE This function consumes an `UnverifiedTransaction` and produces `SignedTransaction` + /// which implies that a heavy check of the signature is performed here. + fn verify_transaction_unordered(&self, t: UnverifiedTransaction, header: &Header) -> Result { self.machine().verify_transaction_unordered(t, header) } - /// Additional verification for transactions in blocks. - // TODO: Add flags for which bits of the transaction to check. - // TODO: consider including State in the params. - fn verify_transaction_basic(&self, t: &UnverifiedTransaction, header: &Header) -> Result<(), Error> { + /// Perform basic/cheap transaction verification. + /// + /// This should include all cheap checks that can be done before + /// actually checking the signature, like chain-replay protection. + /// + /// NOTE This is done before the signature is recovered so avoid + /// doing any state-touching checks that might be expensive. + /// + /// TODO: Add flags for which bits of the transaction to check. + /// TODO: consider including State in the params. + fn verify_transaction_basic(&self, t: &UnverifiedTransaction, header: &Header) -> Result<(), transaction::Error> { self.machine().verify_transaction_basic(t, header) } @@ -389,6 +455,11 @@ pub trait EthEngine: Engine<::machine::EthereumMachine> { fn additional_params(&self) -> HashMap { self.machine().additional_params() } + + /// Performs pre-validation of RLP decoded transaction before other processing + fn decode_transaction(&self, transaction: &[u8]) -> Result { + self.machine().decode_transaction(transaction) + } } // convenience wrappers for existing functions. diff --git a/ethcore/src/engines/null_engine.rs b/ethcore/src/engines/null_engine.rs index f20a9cdfd9d1df07f6439f1ac6940b10fbf4b25f..f9e698307d5123e0f7c7f0d5753d990da2ef5120 100644 --- a/ethcore/src/engines/null_engine.rs +++ b/ethcore/src/engines/null_engine.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -16,8 +16,10 @@ use ethereum_types::U256; use engines::Engine; +use engines::block_reward::{self, RewardKind}; use header::BlockNumber; -use parity_machine::{Header, LiveBlock, WithBalances}; +use machine::WithRewards; +use parity_machine::{Header, LiveBlock, WithBalances, TotalScoredHeader}; /// Params for a null engine. #[derive(Clone, Default)] @@ -56,7 +58,10 @@ impl Default for NullEngine { } } -impl Engine for NullEngine { +impl Engine for NullEngine + where M::ExtendedHeader: TotalScoredHeader, + ::Value: Ord +{ fn name(&self) -> &str { "NullEngine" } @@ -74,26 +79,20 @@ impl Engine for NullEngine { let n_uncles = LiveBlock::uncles(&*block).len(); + let mut rewards = Vec::new(); + // Bestow block reward let result_block_reward = reward + reward.shr(5) * U256::from(n_uncles); - let mut uncle_rewards = Vec::with_capacity(n_uncles); - - self.machine.add_balance(block, &author, &result_block_reward)?; + rewards.push((author, RewardKind::Author, result_block_reward)); // bestow uncle rewards. for u in LiveBlock::uncles(&*block) { let uncle_author = u.author(); let result_uncle_reward = (reward * U256::from(8 + u.number() - number)).shr(3); - - uncle_rewards.push((*uncle_author, result_uncle_reward)); + rewards.push((*uncle_author, RewardKind::Uncle, result_uncle_reward)); } - for &(ref a, ref reward) in &uncle_rewards { - self.machine.add_balance(block, a, reward)?; - } - - // note and trace. - self.machine.note_rewards(block, &[(author, result_block_reward)], &uncle_rewards) + block_reward::apply_block_rewards(&rewards, block, &self.machine) } fn maximum_uncle_count(&self, _block: BlockNumber) -> usize { 2 } @@ -105,4 +104,8 @@ impl Engine for NullEngine { fn snapshot_components(&self) -> Option> { Some(Box::new(::snapshot::PowSnapshot::new(10000, 10000))) } + + fn fork_choice(&self, new: &M::ExtendedHeader, current: &M::ExtendedHeader) -> super::ForkChoice { + super::total_difficulty_fork_choice(new, current) + } } diff --git a/ethcore/src/engines/signer.rs b/ethcore/src/engines/signer.rs index d9e97fee06ecc6ebe8e6538356fff252d6df2b16..965b619c7d2bda0a3a7e32c221d0af0aa7aa011d 100644 --- a/ethcore/src/engines/signer.rs +++ b/ethcore/src/engines/signer.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/src/engines/tendermint/message.rs b/ethcore/src/engines/tendermint/message.rs index 1f71f41bcb508c3dbc951219da9de44d03ef9328..ba8e4390ec14a9cedb568fe278e7acc328eee00b 100644 --- a/ethcore/src/engines/tendermint/message.rs +++ b/ethcore/src/engines/tendermint/message.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -23,7 +23,7 @@ use bytes::Bytes; use super::{Height, View, BlockHash, Step}; use error::Error; use header::Header; -use rlp::{Rlp, UntrustedRlp, RlpStream, Encodable, Decodable, DecoderError}; +use rlp::{Rlp, RlpStream, Encodable, Decodable, DecoderError}; use ethkey::{recover, public_to_address}; use super::super::vote_collector::Message; @@ -43,7 +43,6 @@ pub struct VoteStep { pub step: Step, } - impl VoteStep { pub fn new(height: Height, view: View, step: Step) -> Self { VoteStep { height: height, view: view, step: step } @@ -61,12 +60,12 @@ impl VoteStep { /// Header consensus view. pub fn consensus_view(header: &Header) -> Result { let view_rlp = header.seal().get(0).expect("seal passed basic verification; seal has 3 fields; qed"); - UntrustedRlp::new(view_rlp.as_slice()).as_val() + Rlp::new(view_rlp.as_slice()).as_val() } /// Proposal signature. pub fn proposal_signature(header: &Header) -> Result { - UntrustedRlp::new(header.seal().get(1).expect("seal passed basic verification; seal has 3 fields; qed").as_slice()).as_val() + Rlp::new(header.seal().get(1).expect("seal passed basic verification; seal has 3 fields; qed").as_slice()).as_val() } impl Message for ConsensusMessage { @@ -100,7 +99,7 @@ impl ConsensusMessage { pub fn verify(&self) -> Result { let full_rlp = ::rlp::encode(self); - let block_info = Rlp::new(&full_rlp).at(1); + let block_info = Rlp::new(&full_rlp).at(1)?; let public_key = recover(&self.signature.into(), &keccak(block_info.as_raw()))?; Ok(public_to_address(&public_key)) } @@ -142,7 +141,7 @@ impl Step { } impl Decodable for Step { - fn decode(rlp: &UntrustedRlp) -> Result { + fn decode(rlp: &Rlp) -> Result { match rlp.as_val()? { 0u8 => Ok(Step::Propose), 1 => Ok(Step::Prevote), @@ -160,7 +159,7 @@ impl Encodable for Step { /// (signature, (height, view, step, block_hash)) impl Decodable for ConsensusMessage { - fn decode(rlp: &UntrustedRlp) -> Result { + fn decode(rlp: &Rlp) -> Result { let m = rlp.at(1)?; let block_message: H256 = m.val_at(3)?; Ok(ConsensusMessage { @@ -234,7 +233,7 @@ mod tests { }; let raw_rlp = ::rlp::encode(&message).into_vec(); let rlp = Rlp::new(&raw_rlp); - assert_eq!(message, rlp.as_val()); + assert_eq!(Ok(message), rlp.as_val()); let message = ConsensusMessage { signature: H520::default(), @@ -247,7 +246,7 @@ mod tests { }; let raw_rlp = ::rlp::encode(&message); let rlp = Rlp::new(&raw_rlp); - assert_eq!(message, rlp.as_val()); + assert_eq!(Ok(message), rlp.as_val()); } #[test] @@ -260,7 +259,7 @@ mod tests { let raw_rlp = message_full_rlp(&tap.sign(addr, None, keccak(&mi)).unwrap().into(), &mi); - let rlp = UntrustedRlp::new(&raw_rlp); + let rlp = Rlp::new(&raw_rlp); let message: ConsensusMessage = rlp.as_val().unwrap(); match message.verify() { Ok(a) if a == addr => {}, _ => panic!(), }; } diff --git a/ethcore/src/engines/tendermint/mod.rs b/ethcore/src/engines/tendermint/mod.rs index 4d0656749a35fc6c35beef3ab116835058d7c801..967ef482a69a208c5252451db1ffa8de12aea687 100644 --- a/ethcore/src/engines/tendermint/mod.rs +++ b/ethcore/src/engines/tendermint/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -27,7 +27,7 @@ mod params; use std::sync::{Weak, Arc}; use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering}; -use std::collections::{HashSet, BTreeMap}; +use std::collections::HashSet; use hash::keccak; use ethereum_types::{H256, H520, U128, U256, Address}; use parking_lot::RwLock; @@ -35,12 +35,13 @@ use unexpected::{OutOfBounds, Mismatch}; use client::EngineClient; use bytes::Bytes; use error::{Error, BlockError}; -use header::{Header, BlockNumber}; -use rlp::UntrustedRlp; -use ethkey::{Message, public_to_address, recover, Signature}; +use header::{Header, BlockNumber, ExtendedHeader}; +use rlp::Rlp; +use ethkey::{self, Message, Signature}; use account_provider::AccountProvider; use block::*; use engines::{Engine, Seal, EngineError, ConstructedVerifier}; +use engines::block_reward::{self, RewardKind}; use io::IoService; use super::signer::EngineSigner; use super::validator_set::{ValidatorSet, SimpleList}; @@ -118,7 +119,7 @@ impl super::EpochVerifier for EpochVerifier let mut addresses = HashSet::new(); let ref header_signatures_field = header.seal().get(2).ok_or(BlockError::InvalidSeal)?; - for rlp in UntrustedRlp::new(header_signatures_field).iter() { + for rlp in Rlp::new(header_signatures_field).iter() { let signature: H520 = rlp.as_val()?; let address = (self.recover)(&signature.into(), &message)?; @@ -142,8 +143,10 @@ impl super::EpochVerifier for EpochVerifier } fn check_finality_proof(&self, proof: &[u8]) -> Option> { - let header: Header = ::rlp::decode(proof); - self.verify_light(&header).ok().map(|_| vec![header.hash()]) + match ::rlp::decode(proof) { + Ok(header) => self.verify_light(&header).ok().map(|_| vec![header.hash()]), + Err(_) => None // REVIEW: log perhaps? Not sure what the policy is. + } } } @@ -154,7 +157,7 @@ fn combine_proofs(signal_number: BlockNumber, set_proof: &[u8], finality_proof: } fn destructure_proofs(combined: &[u8]) -> Result<(BlockNumber, &[u8], &[u8]), Error> { - let rlp = UntrustedRlp::new(combined); + let rlp = Rlp::new(combined); Ok(( rlp.at(0)?.as_val()?, rlp.at(1)?.data()?, @@ -356,7 +359,6 @@ impl Tendermint { && lock_change_view < self.view.load(AtomicOrdering::SeqCst) } - fn has_enough_any_votes(&self) -> bool { let step_votes = self.votes.count_round_votes(&VoteStep::new(self.height.load(AtomicOrdering::SeqCst), self.view.load(AtomicOrdering::SeqCst), *self.step.read())); self.check_above_threshold(step_votes).is_ok() @@ -449,17 +451,6 @@ impl Engine for Tendermint { fn maximum_uncle_age(&self) -> usize { 0 } - /// Additional engine-specific information for the user/developer concerning `header`. - fn extra_info(&self, header: &Header) -> BTreeMap { - let message = ConsensusMessage::new_proposal(header).expect("Invalid header."); - map![ - "signature".into() => message.signature.to_string(), - "height".into() => message.vote_step.height.to_string(), - "view".into() => message.vote_step.view.to_string(), - "block_hash".into() => message.block_hash.as_ref().map(ToString::to_string).unwrap_or("".into()) - ] - } - fn populate_from_parent(&self, header: &mut Header, parent: &Header) { // Chain scoring: total weight is sqrt(U256::max_value())*height - view let new_difficulty = U256::from(U128::max_value()) @@ -515,12 +506,12 @@ impl Engine for Tendermint { EngineError::MalformedMessage(format!("{:?}", x)) } - let rlp = UntrustedRlp::new(rlp); + let rlp = Rlp::new(rlp); let message: ConsensusMessage = rlp.as_val().map_err(fmt_err)?; if !self.votes.is_old_or_known(&message) { let msg_hash = keccak(rlp.at(1).map_err(fmt_err)?.as_raw()); - let sender = public_to_address( - &recover(&message.signature.into(), &msg_hash).map_err(fmt_err)? + let sender = ethkey::public_to_address( + ðkey::recover(&message.signature.into(), &msg_hash).map_err(fmt_err)? ); if !self.is_authority(&sender) { @@ -538,7 +529,7 @@ impl Engine for Tendermint { Ok(()) } - fn on_new_block(&self, block: &mut ExecutedBlock, epoch_begin: bool) -> Result<(), Error> { + fn on_new_block(&self, block: &mut ExecutedBlock, epoch_begin: bool, _ancestry: &mut Iterator) -> Result<(), Error> { if !epoch_begin { return Ok(()) } // genesis is never a new block, but might as well check. @@ -561,10 +552,13 @@ impl Engine for Tendermint { /// Apply the block reward on finalisation of the block. fn on_close_block(&self, block: &mut ExecutedBlock) -> Result<(), Error>{ - use parity_machine::WithBalances; let author = *block.header().author(); - self.machine.add_balance(block, &author, &self.block_reward)?; - self.machine.note_rewards(block, &[(author, self.block_reward)], &[]) + + block_reward::apply_block_rewards( + &[(author, RewardKind::Author, self.block_reward)], + block, + &self.machine, + ) } fn verify_local_seal(&self, _header: &Header) -> Result<(), Error> { @@ -607,7 +601,7 @@ impl Engine for Tendermint { let precommit_hash = message_hash(vote_step.clone(), header.bare_hash()); let ref signatures_field = header.seal().get(2).expect("block went through verify_block_basic; block has .seal_fields() fields; qed"); let mut origins = HashSet::new(); - for rlp in UntrustedRlp::new(signatures_field).iter() { + for rlp in Rlp::new(signatures_field).iter() { let precommit = ConsensusMessage { signature: rlp.as_val()?, block_hash: Some(header.bare_hash()), @@ -615,7 +609,7 @@ impl Engine for Tendermint { }; let address = match self.votes.get(&precommit) { Some(a) => a, - None => public_to_address(&recover(&precommit.signature.into(), &precommit_hash)?), + None => ethkey::public_to_address(ðkey::recover(&precommit.signature.into(), &precommit_hash)?), }; if !self.validators.contains(header.parent_hash(), &address) { return Err(EngineError::NotAuthorized(address.to_owned()).into()); @@ -670,7 +664,7 @@ impl Engine for Tendermint { let verifier = Box::new(EpochVerifier { subchain_validators: list, recover: |signature: &Signature, message: &Message| { - Ok(public_to_address(&::ethkey::recover(&signature, &message)?)) + Ok(ethkey::public_to_address(ðkey::recover(&signature, &message)?)) }, }); @@ -691,7 +685,7 @@ impl Engine for Tendermint { } fn sign(&self, hash: H256) -> Result { - self.signer.read().sign(hash).map_err(Into::into) + Ok(self.signer.read().sign(hash)?) } fn snapshot_components(&self) -> Option> { @@ -770,6 +764,10 @@ impl Engine for Tendermint { *self.client.write() = Some(client.clone()); self.validators.register_client(client); } + + fn fork_choice(&self, new: &ExtendedHeader, current: &ExtendedHeader) -> super::ForkChoice { + super::total_difficulty_fork_choice(new, current) + } } #[cfg(test)] @@ -779,11 +777,11 @@ mod tests { use ethereum_types::Address; use bytes::Bytes; use block::*; - use error::{Error, BlockError}; + use error::{Error, ErrorKind, BlockError}; use header::Header; use client::ChainInfo; use miner::MinerService; - use tests::helpers::{ + use test_helpers::{ TestNotify, get_temp_state_db, generate_dummy_client, generate_dummy_client_with_spec_and_accounts }; @@ -805,7 +803,7 @@ mod tests { let db = spec.ensure_db_good(db, &Default::default()).unwrap(); let genesis_header = spec.genesis_header(); let last_hashes = Arc::new(vec![genesis_header.hash()]); - let b = OpenBlock::new(spec.engine.as_ref(), Default::default(), false, db.boxed_clone(), &genesis_header, last_hashes, proposer, (3141562.into(), 31415620.into()), vec![], false).unwrap(); + let b = OpenBlock::new(spec.engine.as_ref(), Default::default(), false, db.boxed_clone(), &genesis_header, last_hashes, proposer, (3141562.into(), 31415620.into()), vec![], false, &mut Vec::new().into_iter()).unwrap(); let b = b.close(); if let Seal::Proposal(seal) = spec.engine.generate_seal(b.block(), &genesis_header) { (b, seal) @@ -866,7 +864,7 @@ mod tests { let verify_result = engine.verify_block_basic(&header); match verify_result { - Err(Error::Block(BlockError::InvalidSealArity(_))) => {}, + Err(Error(ErrorKind::Block(BlockError::InvalidSealArity(_)), _)) => {}, Err(_) => { panic!("should be block seal-arity mismatch error (got {:?})", verify_result); }, _ => { panic!("Should be error, got Ok"); }, } @@ -896,7 +894,7 @@ mod tests { header.set_seal(seal); // Bad proposer. match engine.verify_block_external(&header) { - Err(Error::Engine(EngineError::NotProposer(_))) => {}, + Err(Error(ErrorKind::Engine(EngineError::NotProposer(_)), _)) => {}, _ => panic!(), } @@ -906,7 +904,7 @@ mod tests { header.set_seal(seal); // Not authority. match engine.verify_block_external(&header) { - Err(Error::Engine(EngineError::NotAuthorized(_))) => {}, + Err(Error(ErrorKind::Engine(EngineError::NotAuthorized(_)), _)) => {}, _ => panic!(), }; engine.stop(); @@ -936,7 +934,7 @@ mod tests { // One good signature is not enough. match engine.verify_block_external(&header) { - Err(Error::Engine(EngineError::BadSealFieldSize(_))) => {}, + Err(Error(ErrorKind::Engine(EngineError::BadSealFieldSize(_)), _)) => {}, _ => panic!(), } @@ -956,7 +954,7 @@ mod tests { // One good and one bad signature. match engine.verify_block_external(&header) { - Err(Error::Engine(EngineError::NotAuthorized(_))) => {}, + Err(Error(ErrorKind::Engine(EngineError::NotAuthorized(_)), _)) => {}, _ => panic!(), }; engine.stop(); @@ -1027,7 +1025,7 @@ mod tests { let client = generate_dummy_client_with_spec_and_accounts(Spec::new_test_tendermint, Some(tap.clone())); let engine = client.engine(); - client.miner().set_engine_signer(v1.clone(), "1".into()).unwrap(); + client.miner().set_author(v1.clone(), Some("1".into())).unwrap(); let notify = Arc::new(TestNotify::default()); client.add_notify(notify.clone()); @@ -1103,7 +1101,7 @@ mod tests { } else if *s == signature0 { Ok(voter) } else { - Err(Error::Ethkey(EthkeyError::InvalidSignature)) + Err(ErrorKind::Ethkey(EthkeyError::InvalidSignature).into()) } } }, @@ -1111,7 +1109,7 @@ mod tests { // One good signature is not enough. match epoch_verifier.verify_light(&header) { - Err(Error::Engine(EngineError::BadSealFieldSize(_))) => {}, + Err(Error(ErrorKind::Engine(EngineError::BadSealFieldSize(_)), _)) => {}, _ => panic!(), } @@ -1128,7 +1126,7 @@ mod tests { // One good and one bad signature. match epoch_verifier.verify_light(&header) { - Err(Error::Ethkey(EthkeyError::InvalidSignature)) => {}, + Err(Error(ErrorKind::Ethkey(EthkeyError::InvalidSignature), _)) => {}, _ => panic!(), }; diff --git a/ethcore/src/engines/tendermint/params.rs b/ethcore/src/engines/tendermint/params.rs index c1fd39eb1ca53bbb942a6ff281cae5e08f9933dc..fbd3839caddd6936a4f693ccd5040038bb6bd936 100644 --- a/ethcore/src/engines/tendermint/params.rs +++ b/ethcore/src/engines/tendermint/params.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/src/engines/transition.rs b/ethcore/src/engines/transition.rs index dc745b6e3981b2376977e49e4bdf5997d6a9b7f1..ddc9a70628f880f3e1f1ee14516799677ed6b032 100644 --- a/ethcore/src/engines/transition.rs +++ b/ethcore/src/engines/transition.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -51,8 +51,7 @@ impl TransitionHandler where S: Sync + Send + Clone { pub const ENGINE_TIMEOUT_TOKEN: TimerToken = 23; fn set_timeout(io: &IoContext, timeout: Duration) { - let ms = timeout.as_secs() * 1_000 + timeout.subsec_nanos() as u64 / 1_000_000; - io.register_timer_once(ENGINE_TIMEOUT_TOKEN, ms) + io.register_timer_once(ENGINE_TIMEOUT_TOKEN, timeout) .unwrap_or_else(|e| warn!(target: "engine", "Failed to set consensus step timeout: {}.", e)) } diff --git a/ethcore/src/engines/validator_set/contract.rs b/ethcore/src/engines/validator_set/contract.rs index 0e27d594d548aa0159ffd6c6d969f893cb34840e..c44f2ab303a87e82c22878941d71a8536f237784 100644 --- a/ethcore/src/engines/validator_set/contract.rs +++ b/ethcore/src/engines/validator_set/contract.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -145,8 +145,8 @@ mod tests { use account_provider::AccountProvider; use miner::MinerService; use types::ids::BlockId; + use test_helpers::generate_dummy_client_with_spec_and_accounts; use client::{BlockChainClient, ChainInfo, BlockInfo, CallContract}; - use tests::helpers::generate_dummy_client_with_spec_and_accounts; use super::super::ValidatorSet; use super::ValidatorContract; @@ -169,8 +169,8 @@ mod tests { let validator_contract = "0000000000000000000000000000000000000005".parse::
().unwrap(); // Make sure reporting can be done. - client.miner().set_gas_floor_target(1_000_000.into()); - client.miner().set_engine_signer(v1, "".into()).unwrap(); + client.miner().set_gas_range_target((1_000_000.into(), 1_000_000.into())); + client.miner().set_author(v1, Some("".into())).unwrap(); // Check a block that is a bit in future, reject it but don't report the validator. let mut header = Header::default(); @@ -201,7 +201,7 @@ mod tests { "0000000000000000000000007d577a597b2742b498cb5cf0c26cdcd726d39e6e" ); // Simulate a misbehaving validator by handling a double proposal. - let header = client.best_block_header().decode(); + let header = client.best_block_header(); assert!(client.engine().verify_block_family(&header, &header).is_err()); // Seal a block. client.engine().step(); diff --git a/ethcore/src/engines/validator_set/mod.rs b/ethcore/src/engines/validator_set/mod.rs index de2164f918f86bf93418bf603786baccca5d0317..26b57d78f243197f5f48ceb2c1a39649989c7556 100644 --- a/ethcore/src/engines/validator_set/mod.rs +++ b/ethcore/src/engines/validator_set/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -38,9 +38,7 @@ pub use self::simple_list::SimpleList; use self::contract::ValidatorContract; use self::safe_contract::ValidatorSafeContract; use self::multi::Multi; - -/// A system-calling closure. Enacts calls on a block's state from the system address. -pub type SystemCall<'a> = FnMut(Address, Bytes) -> Result + 'a; +use super::SystemCall; /// Creates a validator set from spec. pub fn new_validator_set(spec: ValidatorSpec) -> Box { diff --git a/ethcore/src/engines/validator_set/multi.rs b/ethcore/src/engines/validator_set/multi.rs index 2794b57a2a43361d73d6ae8e18987368612a67cb..3ac58cd4dc7acf1cdb3fcbfb5c58a1890358ced5 100644 --- a/ethcore/src/engines/validator_set/multi.rs +++ b/ethcore/src/engines/validator_set/multi.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -155,7 +155,7 @@ mod tests { use header::Header; use miner::MinerService; use spec::Spec; - use tests::helpers::{generate_dummy_client_with_spec_and_accounts, generate_dummy_client_with_spec_and_data}; + use test_helpers::{generate_dummy_client_with_spec_and_accounts, generate_dummy_client_with_spec_and_data}; use types::ids::BlockId; use ethereum_types::Address; @@ -171,22 +171,22 @@ mod tests { client.engine().register_client(Arc::downgrade(&client) as _); // Make sure txs go through. - client.miner().set_gas_floor_target(1_000_000.into()); + client.miner().set_gas_range_target((1_000_000.into(), 1_000_000.into())); // Wrong signer for the first block. - client.miner().set_engine_signer(v1, "".into()).unwrap(); + client.miner().set_author(v1, Some("".into())).unwrap(); client.transact_contract(Default::default(), Default::default()).unwrap(); ::client::EngineClient::update_sealing(&*client); assert_eq!(client.chain_info().best_block_number, 0); // Right signer for the first block. - client.miner().set_engine_signer(v0, "".into()).unwrap(); + client.miner().set_author(v0, Some("".into())).unwrap(); ::client::EngineClient::update_sealing(&*client); assert_eq!(client.chain_info().best_block_number, 1); // This time v0 is wrong. client.transact_contract(Default::default(), Default::default()).unwrap(); ::client::EngineClient::update_sealing(&*client); assert_eq!(client.chain_info().best_block_number, 1); - client.miner().set_engine_signer(v1, "".into()).unwrap(); + client.miner().set_author(v1, Some("".into())).unwrap(); ::client::EngineClient::update_sealing(&*client); assert_eq!(client.chain_info().best_block_number, 2); // v1 is still good. diff --git a/ethcore/src/engines/validator_set/safe_contract.rs b/ethcore/src/engines/validator_set/safe_contract.rs index 490a762a610b2a0f2319e24e64983c62ce1d5c8c..e55a0e3e36a76c8b2757f81e681df790de1086a4 100644 --- a/ethcore/src/engines/validator_set/safe_contract.rs +++ b/ethcore/src/engines/validator_set/safe_contract.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -20,12 +20,12 @@ use std::sync::{Weak, Arc}; use hash::keccak; use ethereum_types::{H256, U256, Address, Bloom}; -use parking_lot::{Mutex, RwLock}; +use parking_lot::RwLock; use bytes::Bytes; use memory_cache::MemoryLruCache; use unexpected::Mismatch; -use rlp::{UntrustedRlp, RlpStream}; +use rlp::{Rlp, RlpStream}; use kvdb::DBValue; use client::EngineClient; @@ -53,19 +53,19 @@ lazy_static! { // only "first" proofs are such. struct StateProof { contract_address: Address, - header: Mutex
, + header: Header, provider: validator_set::ValidatorSet, } impl ::engines::StateDependentProof for StateProof { fn generate_proof(&self, caller: &Call) -> Result, String> { - prove_initial(&self.provider, self.contract_address, &*self.header.lock(), caller) + prove_initial(&self.provider, self.contract_address, &self.header, caller) } fn check_proof(&self, machine: &EthereumMachine, proof: &[u8]) -> Result<(), String> { - let (header, state_items) = decode_first_proof(&UntrustedRlp::new(proof)) + let (header, state_items) = decode_first_proof(&Rlp::new(proof)) .map_err(|e| format!("proof incorrectly encoded: {}", e))?; - if &header != &*self.header.lock(){ + if &header != &self.header { return Err("wrong header in proof".into()); } @@ -145,7 +145,7 @@ fn check_first_proof(machine: &EthereumMachine, provider: &validator_set::Valida }).map_err(|err| err.to_string()) } -fn decode_first_proof(rlp: &UntrustedRlp) -> Result<(Header, Vec), ::error::Error> { +fn decode_first_proof(rlp: &Rlp) -> Result<(Header, Vec), ::error::Error> { let header = rlp.val_at(0)?; let state_items = rlp.at(1)?.iter().map(|x| { let mut val = DBValue::new(); @@ -165,7 +165,7 @@ fn encode_proof(header: &Header, receipts: &[Receipt]) -> Bytes { stream.drain().into_vec() } -fn decode_proof(rlp: &UntrustedRlp) -> Result<(Header, Vec), ::error::Error> { +fn decode_proof(rlp: &Rlp) -> Result<(Header, Vec), ::error::Error> { Ok((rlp.val_at(0)?, rlp.list_at(1)?)) } @@ -325,7 +325,7 @@ impl ValidatorSet for ValidatorSafeContract { debug!(target: "engine", "signalling transition to fresh contract."); let state_proof = Arc::new(StateProof { contract_address: self.contract_address, - header: Mutex::new(header.clone()), + header: header.clone(), provider: validator_set::ValidatorSet::default(), }); return ::engines::EpochChange::Yes(::engines::Proof::WithState(state_proof as Arc<_>)); @@ -357,7 +357,7 @@ impl ValidatorSet for ValidatorSafeContract { fn epoch_set(&self, first: bool, machine: &EthereumMachine, _number: ::header::BlockNumber, proof: &[u8]) -> Result<(SimpleList, Option), ::error::Error> { - let rlp = UntrustedRlp::new(proof); + let rlp = Rlp::new(proof); if first { trace!(target: "engine", "Recovering initial epoch set"); @@ -459,7 +459,7 @@ mod tests { use client::{ChainInfo, BlockInfo, ImportBlock}; use ethkey::Secret; use miner::MinerService; - use tests::helpers::{generate_dummy_client_with_spec_and_accounts, generate_dummy_client_with_spec_and_data}; + use test_helpers::{generate_dummy_client_with_spec_and_accounts, generate_dummy_client_with_spec_and_data}; use super::super::ValidatorSet; use super::{ValidatorSafeContract, EVENT_NAME_HASH}; @@ -484,7 +484,7 @@ mod tests { client.engine().register_client(Arc::downgrade(&client) as _); let validator_contract = "0000000000000000000000000000000000000005".parse::
().unwrap(); - client.miner().set_engine_signer(v1, "".into()).unwrap(); + client.miner().set_author(v1, Some("".into())).unwrap(); // Remove "1" validator. let tx = Transaction { nonce: 0.into(), @@ -512,11 +512,11 @@ mod tests { assert_eq!(client.chain_info().best_block_number, 1); // Switch to the validator that is still there. - client.miner().set_engine_signer(v0, "".into()).unwrap(); + client.miner().set_author(v0, Some("".into())).unwrap(); ::client::EngineClient::update_sealing(&*client); assert_eq!(client.chain_info().best_block_number, 2); // Switch back to the added validator, since the state is updated. - client.miner().set_engine_signer(v1, "".into()).unwrap(); + client.miner().set_author(v1, Some("".into())).unwrap(); let tx = Transaction { nonce: 2.into(), gas_price: 0.into(), diff --git a/ethcore/src/engines/validator_set/simple_list.rs b/ethcore/src/engines/validator_set/simple_list.rs index bb67c9778bf2606fefa3daa8ce5ff4e48873d40b..e1339250ef37e583fa0c2a153bdaf2b212318545 100644 --- a/ethcore/src/engines/validator_set/simple_list.rs +++ b/ethcore/src/engines/validator_set/simple_list.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/src/engines/validator_set/test.rs b/ethcore/src/engines/validator_set/test.rs index a6b89304544f336e39d63720d73d1b5decb94f5e..6459803d191fd0c7ec3b6ec5f573f20134a6157f 100644 --- a/ethcore/src/engines/validator_set/test.rs +++ b/ethcore/src/engines/validator_set/test.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/src/engines/vote_collector.rs b/ethcore/src/engines/vote_collector.rs index 7af66b30c5d27e91d50b821401ae02e6e3757d63..f416d0c3f726bec007fa622547e6beab0b627a14 100644 --- a/ethcore/src/engines/vote_collector.rs +++ b/ethcore/src/engines/vote_collector.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/src/error.rs b/ethcore/src/error.rs index dd4cf498673eef5bd4d27d3d737884792a4d7e8d..ba53b9f93e7747ae1afbfc3c0c61ae77d27883b8 100644 --- a/ethcore/src/error.rs +++ b/ethcore/src/error.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -16,10 +16,11 @@ //! General error types for use in ethcore. -use std::fmt; +use std::{fmt, error}; +use std::time::SystemTime; use kvdb; use ethereum_types::{H256, U256, Address, Bloom}; -use util_error::UtilError; +use util_error::{self, UtilError}; use snappy::InvalidInput; use unexpected::{Mismatch, OutOfBounds}; use trie::TrieError; @@ -81,14 +82,11 @@ pub enum BlockError { /// Receipts trie root header field is invalid. InvalidReceiptsRoot(Mismatch), /// Timestamp header field is invalid. - InvalidTimestamp(OutOfBounds), + InvalidTimestamp(OutOfBounds), /// Timestamp header field is too far in future. - TemporarilyInvalid(OutOfBounds), + TemporarilyInvalid(OutOfBounds), /// Log bloom header field is invalid. InvalidLogBloom(Mismatch), - /// Parent hash field of header is invalid; this is an invalid error indicating a logic flaw in the codebase. - /// TODO: remove and favour an assert!/panic!. - InvalidParentHash(Mismatch), /// Number field of header is invalid. InvalidNumber(Mismatch), /// Block number isn't sensible. @@ -128,10 +126,15 @@ impl fmt::Display for BlockError { InvalidSeal => "Block has invalid seal.".into(), InvalidGasLimit(ref oob) => format!("Invalid gas limit: {}", oob), InvalidReceiptsRoot(ref mis) => format!("Invalid receipts trie root in header: {}", mis), - InvalidTimestamp(ref oob) => format!("Invalid timestamp in header: {}", oob), - TemporarilyInvalid(ref oob) => format!("Future timestamp in header: {}", oob), + InvalidTimestamp(ref oob) => { + let oob = oob.map(|st| st.elapsed().unwrap_or_default().as_secs()); + format!("Invalid timestamp in header: {}", oob) + }, + TemporarilyInvalid(ref oob) => { + let oob = oob.map(|st| st.elapsed().unwrap_or_default().as_secs()); + format!("Future timestamp in header: {}", oob) + }, InvalidLogBloom(ref oob) => format!("Invalid log bloom in header: {}", oob), - InvalidParentHash(ref mis) => format!("Invalid parent hash: {}", mis), InvalidNumber(ref mis) => format!("Invalid number in header: {}", mis), RidiculousNumber(ref oob) => format!("Implausible block number. {}", oob), UnknownParent(ref hash) => format!("Unknown parent: {}", hash), @@ -144,45 +147,68 @@ impl fmt::Display for BlockError { } } -#[derive(Debug, Clone, Copy, PartialEq)] -/// Import to the block queue result -pub enum ImportError { - /// Already in the block chain. - AlreadyInChain, - /// Already in the block queue. - AlreadyQueued, - /// Already marked as bad from a previous import (could mean parent is bad). - KnownBad, +impl error::Error for BlockError { + fn description(&self) -> &str { + "Block error" + } } -impl fmt::Display for ImportError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let msg = match *self { - ImportError::AlreadyInChain => "block already in chain", - ImportError::AlreadyQueued => "block already in the block queue", - ImportError::KnownBad => "block known to be bad", - }; +error_chain! { + types { + ImportError, ImportErrorKind, ImportErrorResultExt, ImportErrorResult; + } - f.write_fmt(format_args!("Block import error ({})", msg)) + errors { + #[doc = "Already in the block chain."] + AlreadyInChain { + description("Block already in chain") + display("Block already in chain") + } + + #[doc = "Already in the block queue"] + AlreadyQueued { + description("block already in the block queue") + display("block already in the block queue") + } + + #[doc = "Already marked as bad from a previous import (could mean parent is bad)."] + KnownBad { + description("block known to be bad") + display("block known to be bad") + } } } -/// Error dedicated to import block function -#[derive(Debug)] -pub enum BlockImportError { - /// Import error - Import(ImportError), - /// Block error - Block(BlockError), - /// Other error - Other(String), + +error_chain! { + types { + BlockImportError, BlockImportErrorKind, BlockImportErrorResultExt; + } + + links { + Import(ImportError, ImportErrorKind) #[doc = "Import error"]; + } + + foreign_links { + Block(BlockError) #[doc = "Block error"]; + Decoder(::rlp::DecoderError) #[doc = "Rlp decoding error"]; + } + + errors { + #[doc = "Other error"] + Other(err: String) { + description("Other error") + display("Other error {}", err) + } + } } impl From for BlockImportError { fn from(e: Error) -> Self { match e { - Error::Block(block_error) => BlockImportError::Block(block_error), - Error::Import(import_error) => BlockImportError::Import(import_error), - _ => BlockImportError::Other(format!("other block import error: {:?}", e)), + Error(ErrorKind::Block(block_error), _) => BlockImportErrorKind::Block(block_error).into(), + Error(ErrorKind::Import(import_error), _) => BlockImportErrorKind::Import(import_error.into()).into(), + Error(ErrorKind::Util(util_error::ErrorKind::Decoder(decoder_err)), _) => BlockImportErrorKind::Decoder(decoder_err).into(), + _ => BlockImportErrorKind::Other(format!("other block import error: {:?}", e)).into(), } } } @@ -199,196 +225,126 @@ pub enum TransactionImportError { impl From for TransactionImportError { fn from(e: Error) -> Self { match e { - Error::Transaction(transaction_error) => TransactionImportError::Transaction(transaction_error), + Error(ErrorKind::Transaction(transaction_error), _) => TransactionImportError::Transaction(transaction_error), _ => TransactionImportError::Other(format!("other block import error: {:?}", e)), } } } -#[derive(Debug)] -/// General error type which should be capable of representing all errors in ethcore. -pub enum Error { - /// Client configuration error. - Client(ClientError), - /// Database error. - Database(kvdb::Error), - /// Error concerning a utility. - Util(UtilError), - /// Error concerning block processing. - Block(BlockError), - /// Unknown engine given. - UnknownEngineName(String), - /// Error concerning EVM code execution. - Execution(ExecutionError), - /// Error concerning transaction processing. - Transaction(TransactionError), - /// Error concerning block import. - Import(ImportError), - /// PoW hash is invalid or out of date. - PowHashInvalid, - /// The value of the nonce or mishash is invalid. - PowInvalid, - /// Error concerning TrieDBs - Trie(TrieError), - /// Io crate error. - Io(IoError), - /// Standard io error. - StdIo(::std::io::Error), - /// Snappy error. - Snappy(InvalidInput), - /// Snapshot error. - Snapshot(SnapshotError), - /// Consensus vote error. - Engine(EngineError), - /// Ethkey error. - Ethkey(EthkeyError), - /// Account Provider error. - AccountProvider(AccountsError), -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Error::Client(ref err) => err.fmt(f), - Error::Database(ref err) => err.fmt(f), - Error::Util(ref err) => err.fmt(f), - Error::Io(ref err) => err.fmt(f), - Error::Block(ref err) => err.fmt(f), - Error::Execution(ref err) => err.fmt(f), - Error::Transaction(ref err) => err.fmt(f), - Error::Import(ref err) => err.fmt(f), - Error::UnknownEngineName(ref name) => - f.write_fmt(format_args!("Unknown engine name ({})", name)), - Error::PowHashInvalid => f.write_str("Invalid or out of date PoW hash."), - Error::PowInvalid => f.write_str("Invalid nonce or mishash"), - Error::Trie(ref err) => err.fmt(f), - Error::StdIo(ref err) => err.fmt(f), - Error::Snappy(ref err) => err.fmt(f), - Error::Snapshot(ref err) => err.fmt(f), - Error::Engine(ref err) => err.fmt(f), - Error::Ethkey(ref err) => err.fmt(f), - Error::AccountProvider(ref err) => err.fmt(f), - } +error_chain! { + types { + Error, ErrorKind, ErrorResultExt, EthcoreResult; } -} - -/// Result of import block operation. -pub type ImportResult = Result; -impl From for Error { - fn from(err: ClientError) -> Error { - match err { - ClientError::Trie(err) => Error::Trie(err), - _ => Error::Client(err) - } + links { + Database(kvdb::Error, kvdb::ErrorKind) #[doc = "Database error."]; + Util(UtilError, util_error::ErrorKind) #[doc = "Error concerning a utility"]; + Import(ImportError, ImportErrorKind) #[doc = "Error concerning block import." ]; } -} - -impl From for Error { - fn from(err: kvdb::Error) -> Error { - Error::Database(err) + + foreign_links { + Io(IoError) #[doc = "Io create error"]; + StdIo(::std::io::Error) #[doc = "Error concerning the Rust standard library's IO subsystem."]; + Trie(TrieError) #[doc = "Error concerning TrieDBs."]; + Execution(ExecutionError) #[doc = "Error concerning EVM code execution."]; + Block(BlockError) #[doc = "Error concerning block processing."]; + Transaction(TransactionError) #[doc = "Error concerning transaction processing."]; + Snappy(InvalidInput) #[doc = "Snappy error."]; + Engine(EngineError) #[doc = "Consensus vote error."]; + Ethkey(EthkeyError) #[doc = "Ethkey error."]; } -} -impl From for Error { - fn from(err: TransactionError) -> Error { - Error::Transaction(err) - } -} + errors { + #[doc = "Client configuration error."] + Client(err: ClientError) { + description("Client configuration error.") + display("Client configuration error {}", err) + } -impl From for Error { - fn from(err: ImportError) -> Error { - Error::Import(err) - } -} + #[doc = "Snapshot error."] + Snapshot(err: SnapshotError) { + description("Snapshot error.") + display("Snapshot error {}", err) + } -impl From for Error { - fn from(err: BlockError) -> Error { - Error::Block(err) - } -} + #[doc = "Account Provider error"] + AccountProvider(err: AccountsError) { + description("Accounts Provider error") + display("Accounts Provider error {}", err) + } -impl From for Error { - fn from(err: ExecutionError) -> Error { - Error::Execution(err) - } -} + #[doc = "PoW hash is invalid or out of date."] + PowHashInvalid { + description("PoW hash is invalid or out of date.") + display("PoW hash is invalid or out of date.") + } + + #[doc = "The value of the nonce or mishash is invalid."] + PowInvalid { + description("The value of the nonce or mishash is invalid.") + display("The value of the nonce or mishash is invalid.") + } -impl From<::rlp::DecoderError> for Error { - fn from(err: ::rlp::DecoderError) -> Error { - Error::Util(UtilError::from(err)) - } -} + #[doc = "Unknown engine given"] + UnknownEngineName(name: String) { + description("Unknown engine name") + display("Unknown engine name ({})", name) + } -impl From for Error { - fn from(err: UtilError) -> Error { - Error::Util(err) + #[doc = "RLP decoding errors"] + Decoder(err: ::rlp::DecoderError) { + description("decoding value failed") + display("decoding value failed with error: {}", err) + } } } -impl From for Error { - fn from(err: IoError) -> Error { - Error::Io(err) +/// Result of import block operation. +pub type ImportResult = EthcoreResult; + +impl From for Error { + fn from(err: ClientError) -> Error { + match err { + ClientError::Trie(err) => ErrorKind::Trie(err).into(), + _ => ErrorKind::Client(err).into() + } } } -impl From for Error { - fn from(err: TrieError) -> Error { - Error::Trie(err) - } +impl From for Error { + fn from(err: AccountsError) -> Error { + ErrorKind::AccountProvider(err).into() + } } -impl From<::std::io::Error> for Error { - fn from(err: ::std::io::Error) -> Error { - Error::StdIo(err) +impl From<::rlp::DecoderError> for Error { + fn from(err: ::rlp::DecoderError) -> Error { + ErrorKind::Decoder(err).into() } } impl From for Error { fn from(err: BlockImportError) -> Error { match err { - BlockImportError::Block(e) => Error::Block(e), - BlockImportError::Import(e) => Error::Import(e), - BlockImportError::Other(s) => Error::Util(UtilError::from(s)), + BlockImportError(BlockImportErrorKind::Block(e), _) => ErrorKind::Block(e).into(), + BlockImportError(BlockImportErrorKind::Import(e), _) => ErrorKind::Import(e).into(), + BlockImportError(BlockImportErrorKind::Other(s), _) => UtilError::from(s).into(), + _ => ErrorKind::Msg(format!("other block import error: {:?}", err)).into(), } } } -impl From<::snappy::InvalidInput> for Error { - fn from(err: ::snappy::InvalidInput) -> Error { - Error::Snappy(err) - } -} - impl From for Error { fn from(err: SnapshotError) -> Error { match err { - SnapshotError::Io(err) => Error::StdIo(err), - SnapshotError::Trie(err) => Error::Trie(err), + SnapshotError::Io(err) => ErrorKind::StdIo(err).into(), + SnapshotError::Trie(err) => ErrorKind::Trie(err).into(), SnapshotError::Decoder(err) => err.into(), - other => Error::Snapshot(other), + other => ErrorKind::Snapshot(other).into(), } } } -impl From for Error { - fn from(err: EngineError) -> Error { - Error::Engine(err) - } -} - -impl From for Error { - fn from(err: EthkeyError) -> Error { - Error::Ethkey(err) - } -} - -impl From for Error { - fn from(err: AccountsError) -> Error { - Error::AccountProvider(err) - } -} - impl From> for Error where Error: From { fn from(err: Box) -> Error { Error::from(*err) diff --git a/ethcore/src/ethereum/denominations.rs b/ethcore/src/ethereum/denominations.rs index 4892770df1e9dc3324e8a58485719810040888e5..4c51932543c3b4dc7da823c39a4800f12cbb38fc 100644 --- a/ethcore/src/ethereum/denominations.rs +++ b/ethcore/src/ethereum/denominations.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -35,4 +35,3 @@ pub fn shannon() -> U256 { U256::exp10(9) } #[inline] /// 1 Wei in Wei pub fn wei() -> U256 { U256::exp10(0) } - diff --git a/ethcore/src/ethereum/ethash.rs b/ethcore/src/ethereum/ethash.rs index 68a07d0cda30f552e7fd6dea06f54a0dc2653889..b51da58fec66fad81de0af83d34f48fee28ecd36 100644 --- a/ethcore/src/ethereum/ethash.rs +++ b/ethcore/src/ethereum/ethash.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -19,15 +19,16 @@ use std::cmp; use std::collections::BTreeMap; use std::sync::Arc; use hash::{KECCAK_EMPTY_LIST_RLP}; +use engines::block_reward::{self, RewardKind}; use ethash::{quick_get_difficulty, slow_hash_block_number, EthashManager, OptimizeFor}; use ethereum_types::{H256, H64, U256, Address}; use unexpected::{OutOfBounds, Mismatch}; use block::*; use error::{BlockError, Error}; -use header::{Header, BlockNumber}; +use header::{Header, BlockNumber, ExtendedHeader}; use engines::{self, Engine}; use ethjson; -use rlp::UntrustedRlp; +use rlp::Rlp; use machine::EthereumMachine; /// Number of blocks in an ethash snapshot. @@ -59,8 +60,8 @@ impl Seal { ).into()); } - let mix_hash = UntrustedRlp::new(seal[0].as_ref()).as_val::()?; - let nonce = UntrustedRlp::new(seal[1].as_ref()).as_val::()?; + let mix_hash = Rlp::new(seal[0].as_ref()).as_val::()?; + let nonce = Rlp::new(seal[1].as_ref()).as_val::()?; let seal = Seal { mix_hash, nonce, @@ -221,23 +222,17 @@ impl Engine for Arc { header.set_difficulty(difficulty); } - fn on_new_block( - &self, - _block: &mut ExecutedBlock, - _begins_epoch: bool, - ) -> Result<(), Error> { - Ok(()) - } - /// Apply the block reward on finalisation of the block. /// This assumes that all uncles are valid uncles (i.e. of at least one generation before the current). fn on_close_block(&self, block: &mut ExecutedBlock) -> Result<(), Error> { use std::ops::Shr; - use parity_machine::{LiveBlock, WithBalances}; + use parity_machine::LiveBlock; let author = *LiveBlock::header(&*block).author(); let number = LiveBlock::header(&*block).number(); + let mut rewards = Vec::new(); + // Applies EIP-649 reward. let reward = if number >= self.ethash_params.eip649_transition { self.ethash_params.eip649_reward.unwrap_or(self.ethash_params.block_reward) @@ -253,20 +248,21 @@ impl Engine for Arc { // Bestow block rewards. let mut result_block_reward = reward + reward.shr(5) * U256::from(n_uncles); - let mut uncle_rewards = Vec::with_capacity(n_uncles); if number >= self.ethash_params.mcip3_transition { result_block_reward = self.ethash_params.mcip3_miner_reward; + let ubi_contract = self.ethash_params.mcip3_ubi_contract; let ubi_reward = self.ethash_params.mcip3_ubi_reward; let dev_contract = self.ethash_params.mcip3_dev_contract; let dev_reward = self.ethash_params.mcip3_dev_reward; - self.machine.add_balance(block, &author, &result_block_reward)?; - self.machine.add_balance(block, &ubi_contract, &ubi_reward)?; - self.machine.add_balance(block, &dev_contract, &dev_reward)?; + rewards.push((author, RewardKind::Author, result_block_reward)); + rewards.push((ubi_contract, RewardKind::External, ubi_reward)); + rewards.push((dev_contract, RewardKind::External, dev_reward)); + } else { - self.machine.add_balance(block, &author, &result_block_reward)?; + rewards.push((author, RewardKind::Author, result_block_reward)); } // Bestow uncle rewards. @@ -278,15 +274,10 @@ impl Engine for Arc { reward.shr(5) }; - uncle_rewards.push((*uncle_author, result_uncle_reward)); - } - - for &(ref a, ref reward) in &uncle_rewards { - self.machine.add_balance(block, a, reward)?; + rewards.push((*uncle_author, RewardKind::Uncle, result_uncle_reward)); } - // Note and trace. - self.machine.note_rewards(block, &[(author, result_block_reward)], &uncle_rewards) + block_reward::apply_block_rewards(&rewards, block, &self.machine) } fn verify_local_seal(&self, header: &Header) -> Result<(), Error> { @@ -365,6 +356,10 @@ impl Engine for Arc { fn snapshot_components(&self) -> Option> { Some(Box::new(::snapshot::PowSnapshot::new(SNAPSHOT_BLOCKS, MAX_SNAPSHOT_BLOCKS))) } + + fn fork_choice(&self, new: &ExtendedHeader, current: &ExtendedHeader) -> engines::ForkChoice { + engines::total_difficulty_fork_choice(new, current) + } } impl Ethash { @@ -487,8 +482,8 @@ mod tests { use std::sync::Arc; use ethereum_types::{H64, H256, U256, Address}; use block::*; - use tests::helpers::get_temp_state_db; - use error::{BlockError, Error}; + use test_helpers::get_temp_state_db; + use error::{BlockError, Error, ErrorKind}; use header::Header; use spec::Spec; use engines::Engine; @@ -539,7 +534,7 @@ mod tests { let genesis_header = spec.genesis_header(); let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); let last_hashes = Arc::new(vec![genesis_header.hash()]); - let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![], false).unwrap(); + let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![], false, &mut Vec::new().into_iter()).unwrap(); let b = b.close(); assert_eq!(b.state().balance(&Address::zero()).unwrap(), U256::from_str("4563918244f40000").unwrap()); } @@ -588,7 +583,7 @@ mod tests { let genesis_header = spec.genesis_header(); let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); let last_hashes = Arc::new(vec![genesis_header.hash()]); - let mut b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![], false).unwrap(); + let mut b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![], false, &mut Vec::new().into_iter()).unwrap(); let mut uncle = Header::new(); let uncle_author: Address = "ef2d6d194084c2de36e0dabfce45d046b37d1106".into(); uncle.set_author(uncle_author); @@ -606,7 +601,7 @@ mod tests { let genesis_header = spec.genesis_header(); let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); let last_hashes = Arc::new(vec![genesis_header.hash()]); - let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![], false).unwrap(); + let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![], false, &mut Vec::new().into_iter()).unwrap(); let b = b.close(); let ubi_contract: Address = "00efdd5883ec628983e9063c7d969fe268bbf310".into(); @@ -640,7 +635,7 @@ mod tests { let verify_result = engine.verify_block_basic(&header); match verify_result { - Err(Error::Block(BlockError::InvalidSealArity(_))) => {}, + Err(Error(ErrorKind::Block(BlockError::InvalidSealArity(_)), _)) => {}, Err(_) => { panic!("should be block seal-arity mismatch error (got {:?})", verify_result); }, _ => { panic!("Should be error, got Ok"); }, } @@ -655,7 +650,7 @@ mod tests { let verify_result = engine.verify_block_basic(&header); match verify_result { - Err(Error::Block(BlockError::DifficultyOutOfBounds(_))) => {}, + Err(Error(ErrorKind::Block(BlockError::DifficultyOutOfBounds(_)), _)) => {}, Err(_) => { panic!("should be block difficulty error (got {:?})", verify_result); }, _ => { panic!("Should be error, got Ok"); }, } @@ -671,7 +666,7 @@ mod tests { let verify_result = engine.verify_block_basic(&header); match verify_result { - Err(Error::Block(BlockError::InvalidProofOfWork(_))) => {}, + Err(Error(ErrorKind::Block(BlockError::InvalidProofOfWork(_)), _)) => {}, Err(_) => { panic!("should be invalid proof of work error (got {:?})", verify_result); }, _ => { panic!("Should be error, got Ok"); }, } @@ -685,7 +680,7 @@ mod tests { let verify_result = engine.verify_block_unordered(&header); match verify_result { - Err(Error::Block(BlockError::InvalidSealArity(_))) => {}, + Err(Error(ErrorKind::Block(BlockError::InvalidSealArity(_)), _)) => {}, Err(_) => { panic!("should be block seal-arity mismatch error (got {:?})", verify_result); }, _ => { panic!("Should be error, got Ok"); }, } @@ -710,7 +705,7 @@ mod tests { let verify_result = engine.verify_block_unordered(&header); match verify_result { - Err(Error::Block(BlockError::MismatchedH256SealElement(_))) => {}, + Err(Error(ErrorKind::Block(BlockError::MismatchedH256SealElement(_)), _)) => {}, Err(_) => { panic!("should be invalid 256-bit seal fail (got {:?})", verify_result); }, _ => { panic!("Should be error, got Ok"); }, } @@ -726,7 +721,7 @@ mod tests { let verify_result = engine.verify_block_unordered(&header); match verify_result { - Err(Error::Block(BlockError::InvalidProofOfWork(_))) => {}, + Err(Error(ErrorKind::Block(BlockError::InvalidProofOfWork(_)), _)) => {}, Err(_) => { panic!("should be invalid proof-of-work fail (got {:?})", verify_result); }, _ => { panic!("Should be error, got Ok"); }, } @@ -741,7 +736,7 @@ mod tests { let verify_result = engine.verify_block_family(&header, &parent_header); match verify_result { - Err(Error::Block(BlockError::RidiculousNumber(_))) => {}, + Err(Error(ErrorKind::Block(BlockError::RidiculousNumber(_)), _)) => {}, Err(_) => { panic!("should be invalid block number fail (got {:?})", verify_result); }, _ => { panic!("Should be error, got Ok"); }, } @@ -758,7 +753,7 @@ mod tests { let verify_result = engine.verify_block_family(&header, &parent_header); match verify_result { - Err(Error::Block(BlockError::InvalidDifficulty(_))) => {}, + Err(Error(ErrorKind::Block(BlockError::InvalidDifficulty(_)), _)) => {}, Err(_) => { panic!("should be invalid difficulty fail (got {:?})", verify_result); }, _ => { panic!("Should be error, got Ok"); }, } diff --git a/ethcore/src/ethereum/mod.rs b/ethcore/src/ethereum/mod.rs index e892cf56a4b58d50710545b8bea9a56b7ba9909c..4aab4b71ffb5201c5fbcaa92bf51647bdbbc40db 100644 --- a/ethcore/src/ethereum/mod.rs +++ b/ethcore/src/ethereum/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -61,6 +61,11 @@ pub fn new_expanse<'a, T: Into>>(params: T) -> Spec { load(params.into(), include_bytes!("../../res/ethereum/expanse.json")) } +/// Create a new Tobalaba chain spec. +pub fn new_tobalaba<'a, T: Into>>(params: T) -> Spec { + load(params.into(), include_bytes!("../../res/ethereum/tobalaba.json")) +} + /// Create a new Musicoin mainnet chain spec. pub fn new_musicoin<'a, T: Into>>(params: T) -> Spec { load(params.into(), include_bytes!("../../res/ethereum/musicoin.json")) @@ -71,6 +76,16 @@ pub fn new_ellaism<'a, T: Into>>(params: T) -> Spec { load(params.into(), include_bytes!("../../res/ethereum/ellaism.json")) } +/// Create a new Easthub mainnet chain spec. +pub fn new_easthub<'a, T: Into>>(params: T) -> Spec { + load(params.into(), include_bytes!("../../res/ethereum/easthub.json")) +} + +/// Create a new Ethereum Social mainnet chain spec. +pub fn new_social<'a, T: Into>>(params: T) -> Spec { + load(params.into(), include_bytes!("../../res/ethereum/social.json")) +} + /// Create a new Kovan testnet chain spec. pub fn new_kovan<'a, T: Into>>(params: T) -> Spec { load(params.into(), include_bytes!("../../res/ethereum/kovan.json")) @@ -91,6 +106,9 @@ pub fn new_morden<'a, T: Into>>(params: T) -> Spec { /// Create a new Foundation Frontier-era chain spec as though it never changes to Homestead. pub fn new_frontier_test() -> Spec { load(None, include_bytes!("../../res/ethereum/frontier_test.json")) } +/// Create a new Ropsten chain spec. +pub fn new_ropsten_test() -> Spec { load(None, include_bytes!("../../res/ethereum/ropsten.json")) } + /// Create a new Foundation Homestead-era chain spec as though it never changed from Frontier. pub fn new_homestead_test() -> Spec { load(None, include_bytes!("../../res/ethereum/homestead_test.json")) } @@ -140,7 +158,7 @@ mod tests { use ethereum_types::U256; use state::*; use super::*; - use tests::helpers::get_temp_state_db; + use test_helpers::get_temp_state_db; use views::BlockView; #[test] @@ -164,7 +182,7 @@ mod tests { assert_eq!(morden.state_root(), "f3f4696bbf3b3b07775128eb7a3763279a394e382130f27c21e70233e04946a9".into()); let genesis = morden.genesis_block(); - assert_eq!(BlockView::new(&genesis).header_view().hash(), "0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303".into()); + assert_eq!(view!(BlockView, &genesis).header_view().hash(), "0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303".into()); let _ = morden.engine; } @@ -175,7 +193,7 @@ mod tests { assert_eq!(frontier.state_root(), "d7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544".into()); let genesis = frontier.genesis_block(); - assert_eq!(BlockView::new(&genesis).header_view().hash(), "d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3".into()); + assert_eq!(view!(BlockView, &genesis).header_view().hash(), "d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3".into()); let _ = frontier.engine; } diff --git a/ethcore/src/executed.rs b/ethcore/src/executed.rs index a787209326467ec900f369506acf34d612b9f9be..3d0b9767c4a029b6c50c49c43d15693a178b9b95 100644 --- a/ethcore/src/executed.rs +++ b/ethcore/src/executed.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -24,7 +24,7 @@ use trace::{VMTrace, FlatTrace}; use log_entry::LogEntry; use state_diff::StateDiff; -use std::fmt; +use std::{fmt, error}; /// Transaction execution receipt. #[derive(Debug, PartialEq, Clone)] @@ -148,6 +148,12 @@ impl fmt::Display for ExecutionError { } } +impl error::Error for ExecutionError { + fn description(&self) -> &str { + "Transaction execution error" + } +} + /// Result of executing the transaction. #[derive(PartialEq, Debug, Clone)] pub enum CallError { diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index 8e792f9e2e21e8d678c165843f6b421813015bc8..f375f3c2e88373cfb530728f0188635cefb39768 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -34,10 +34,21 @@ use transaction::{Action, SignedTransaction}; use crossbeam; pub use executed::{Executed, ExecutionResult}; -/// Roughly estimate what stack size each level of evm depth will use -/// TODO [todr] We probably need some more sophisticated calculations here (limit on my machine 132) -/// Maybe something like here: `https://github.com/ethereum/libethereum/blob/4db169b8504f2b87f7d5a481819cfb959fc65f6c/libethereum/ExtVM.cpp` -const STACK_SIZE_PER_DEPTH: usize = 24*1024; +#[cfg(debug_assertions)] +/// Roughly estimate what stack size each level of evm depth will use. (Debug build) +const STACK_SIZE_PER_DEPTH: usize = 128 * 1024; + +#[cfg(not(debug_assertions))] +/// Roughly estimate what stack size each level of evm depth will use. +const STACK_SIZE_PER_DEPTH: usize = 24 * 1024; + +#[cfg(debug_assertions)] +/// Entry stack overhead prior to execution. (Debug build) +const STACK_SIZE_ENTRY_OVERHEAD: usize = 100 * 1024; + +#[cfg(not(debug_assertions))] +/// Entry stack overhead prior to execution. +const STACK_SIZE_ENTRY_OVERHEAD: usize = 20 * 1024; /// Returns new address created from address, nonce, and code hash pub fn contract_address(address_scheme: CreateContractAddress, sender: &Address, nonce: &U256, code: &[u8]) -> (Address, Option) { @@ -237,27 +248,27 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> { let base_gas_required = U256::from(t.gas_required(&schedule)); if t.gas < base_gas_required { - return Err(From::from(ExecutionError::NotEnoughBaseGas { required: base_gas_required, got: t.gas })); + return Err(ExecutionError::NotEnoughBaseGas { required: base_gas_required, got: t.gas }); } if !t.is_unsigned() && check_nonce && schedule.kill_dust != CleanDustMode::Off && !self.state.exists(&sender)? { - return Err(From::from(ExecutionError::SenderMustExist)); + return Err(ExecutionError::SenderMustExist); } let init_gas = t.gas - base_gas_required; // validate transaction nonce if check_nonce && t.nonce != nonce { - return Err(From::from(ExecutionError::InvalidNonce { expected: nonce, got: t.nonce })); + return Err(ExecutionError::InvalidNonce { expected: nonce, got: t.nonce }); } // validate if transaction fits into given block if self.info.gas_used + t.gas > self.info.gas_limit { - return Err(From::from(ExecutionError::BlockGasLimitReached { + return Err(ExecutionError::BlockGasLimitReached { gas_limit: self.info.gas_limit, gas_used: self.info.gas_used, gas: t.gas - })); + }); } // TODO: we might need bigints here, or at least check overflows. @@ -268,7 +279,7 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> { // avoid unaffordable transactions let balance512 = U512::from(balance); if balance512 < total_cost { - return Err(From::from(ExecutionError::NotEnoughCash { required: total_cost, got: balance512 })); + return Err(ExecutionError::NotEnoughCash { required: total_cost, got: balance512 }); } let mut substate = Substate::new(); @@ -332,12 +343,12 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> { tracer: &mut T, vm_tracer: &mut V ) -> vm::Result where T: Tracer, V: VMTracer { - - let depth_threshold = ::io::LOCAL_STACK_SIZE.with(|sz| sz.get() / STACK_SIZE_PER_DEPTH); + let local_stack_size = ::io::LOCAL_STACK_SIZE.with(|sz| sz.get()); + let depth_threshold = local_stack_size.saturating_sub(STACK_SIZE_ENTRY_OVERHEAD) / STACK_SIZE_PER_DEPTH; let static_call = params.call_type == CallType::StaticCall; // Ordinary execution - keep VM in same thread - if (self.depth + 1) % depth_threshold != 0 { + if self.depth != depth_threshold { let vm_factory = self.state.vm_factory(); let mut ext = self.as_externalities(OriginInfo::from(¶ms), unconfirmed_substate, output_policy, tracer, vm_tracer, static_call); trace!(target: "executive", "ext.schedule.have_delegate_call: {}", ext.schedule().have_delegate_call); @@ -345,17 +356,15 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> { return vm.exec(params, &mut ext).finalize(ext); } - // Start in new thread to reset stack - // TODO [todr] No thread builder yet, so we need to reset once for a while - // https://github.com/aturon/crossbeam/issues/16 + // Start in new thread with stack size needed up to max depth crossbeam::scope(|scope| { let vm_factory = self.state.vm_factory(); let mut ext = self.as_externalities(OriginInfo::from(¶ms), unconfirmed_substate, output_policy, tracer, vm_tracer, static_call); - scope.spawn(move || { + scope.builder().stack_size(::std::cmp::max(schedule.max_depth.saturating_sub(depth_threshold) * STACK_SIZE_PER_DEPTH, local_stack_size)).spawn(move || { let mut vm = vm_factory.create(¶ms, &schedule); vm.exec(params, &mut ext).finalize(ext) - }) + }).expect("Sub-thread creation cannot fail; the host might run out of resources; qed") }).join() } @@ -419,8 +428,14 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> { self.state.discard_checkpoint(); output.write(0, &builtin_out_buffer); - // trace only top level calls to builtins to avoid DDoS attacks - if self.depth == 0 { + // Trace only top level calls and calls with balance transfer to builtins. The reason why we don't + // trace all internal calls to builtin contracts is that memcpy (IDENTITY) is a heavily used + // function. + let is_transferred = match params.value { + ActionValue::Transfer(value) => value != U256::zero(), + ActionValue::Apparent(_) => false, + }; + if self.depth == 0 || is_transferred { let mut trace_output = tracer.prepare_trace_output(); if let Some(out) = trace_output.as_mut() { *out = output.to_owned(); @@ -701,7 +716,7 @@ mod tests { use error::ExecutionError; use machine::EthereumMachine; use state::{Substate, CleanupMode}; - use tests::helpers::{get_temp_state_with_factory, get_temp_state}; + use test_helpers::{get_temp_state_with_factory, get_temp_state}; use trace::trace; use trace::{FlatTrace, Tracer, NoopTracer, ExecutiveTracer}; use trace::{VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, VMTracer, NoopVMTracer, ExecutiveVMTracer}; @@ -713,6 +728,12 @@ mod tests { machine } + fn make_byzantium_machine(max_depth: usize) -> EthereumMachine { + let mut machine = ::ethereum::new_byzantium_test_machine(); + machine.set_schedule_creation_rules(Box::new(move |s, _| s.max_depth = max_depth)); + machine + } + #[test] fn test_contract_address() { let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); @@ -721,7 +742,7 @@ mod tests { } // TODO: replace params with transactions! - evm_test!{test_sender_balance: test_sender_balance_jit, test_sender_balance_int} + evm_test!{test_sender_balance: test_sender_balance_int} fn test_sender_balance(factory: Factory) { let sender = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); let address = contract_address(CreateContractAddress::FromSenderAndNonce, &sender, &U256::zero(), &[]).0; @@ -746,13 +767,12 @@ mod tests { assert_eq!(state.storage_at(&address, &H256::new()).unwrap(), H256::from(&U256::from(0xf9u64))); assert_eq!(state.balance(&sender).unwrap(), U256::from(0xf9)); assert_eq!(state.balance(&address).unwrap(), U256::from(0x7)); - // 0 cause contract hasn't returned assert_eq!(substate.contracts_created.len(), 0); // TODO: just test state root. } - evm_test!{test_create_contract_out_of_depth: test_create_contract_out_of_depth_jit, test_create_contract_out_of_depth_int} + evm_test!{test_create_contract_out_of_depth: test_create_contract_out_of_depth_int} fn test_create_contract_out_of_depth(factory: Factory) { // code: // @@ -805,6 +825,76 @@ mod tests { assert_eq!(substate.contracts_created.len(), 0); } + #[test] + fn test_call_to_precompiled_tracing() { + // code: + // + // 60 00 - push 00 out size + // 60 00 - push 00 out offset + // 60 00 - push 00 in size + // 60 00 - push 00 in offset + // 60 01 - push 01 value + // 60 03 - push 03 to + // 61 ffff - push fff gas + // f1 - CALL + + let code = "60006000600060006001600361fffff1".from_hex().unwrap(); + let sender = Address::from_str("4444444444444444444444444444444444444444").unwrap(); + let address = Address::from_str("5555555555555555555555555555555555555555").unwrap(); + + let mut params = ActionParams::default(); + params.address = address.clone(); + params.code_address = address.clone(); + params.sender = sender.clone(); + params.origin = sender.clone(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + params.value = ActionValue::Transfer(U256::from(100)); + params.call_type = CallType::Call; + let mut state = get_temp_state(); + state.add_balance(&sender, &U256::from(100), CleanupMode::NoEmpty).unwrap(); + let info = EnvInfo::default(); + let machine = make_byzantium_machine(5); + let mut substate = Substate::new(); + let mut tracer = ExecutiveTracer::default(); + let mut vm_tracer = ExecutiveVMTracer::toplevel(); + + let mut ex = Executive::new(&mut state, &info, &machine); + let output = BytesRef::Fixed(&mut[0u8;0]); + ex.call(params, &mut substate, output, &mut tracer, &mut vm_tracer).unwrap(); + + assert_eq!(tracer.drain(), vec![FlatTrace { + action: trace::Action::Call(trace::Call { + from: "4444444444444444444444444444444444444444".into(), + to: "5555555555555555555555555555555555555555".into(), + value: 100.into(), + gas: 100_000.into(), + input: vec![], + call_type: CallType::Call + }), + result: trace::Res::Call(trace::CallResult { + gas_used: 33021.into(), + output: vec![] + }), + subtraces: 1, + trace_address: Default::default() + }, FlatTrace { + action: trace::Action::Call(trace::Call { + from: "5555555555555555555555555555555555555555".into(), + to: "0000000000000000000000000000000000000003".into(), + value: 1.into(), + gas: 66560.into(), + input: vec![], + call_type: CallType::Call + }), result: trace::Res::Call(trace::CallResult { + gas_used: 600.into(), + output: vec![] + }), + subtraces: 0, + trace_address: vec![0].into_iter().collect(), + }]); + } + #[test] // Tracing is not suported in JIT fn test_call_to_create() { @@ -1083,7 +1173,7 @@ mod tests { assert_eq!(vm_tracer.drain().unwrap(), expected_vm_trace); } - evm_test!{test_create_contract_value_too_high: test_create_contract_value_too_high_jit, test_create_contract_value_too_high_int} + evm_test!{test_create_contract_value_too_high: test_create_contract_value_too_high_int} fn test_create_contract_value_too_high(factory: Factory) { // code: // @@ -1135,7 +1225,7 @@ mod tests { assert_eq!(substate.contracts_created.len(), 0); } - evm_test!{test_create_contract_without_max_depth: test_create_contract_without_max_depth_jit, test_create_contract_without_max_depth_int} + evm_test!{test_create_contract_without_max_depth: test_create_contract_without_max_depth_int} fn test_create_contract_without_max_depth(factory: Factory) { // code: // @@ -1188,7 +1278,7 @@ mod tests { // test is incorrect, mk // TODO: fix (preferred) or remove - evm_test_ignore!{test_aba_calls: test_aba_calls_jit, test_aba_calls_int} + evm_test_ignore!{test_aba_calls: test_aba_calls_int} fn test_aba_calls(factory: Factory) { // 60 00 - push 0 // 60 00 - push 0 @@ -1248,7 +1338,7 @@ mod tests { // test is incorrect, mk // TODO: fix (preferred) or remove - evm_test_ignore!{test_recursive_bomb1: test_recursive_bomb1_jit, test_recursive_bomb1_int} + evm_test_ignore!{test_recursive_bomb1: test_recursive_bomb1_int} fn test_recursive_bomb1(factory: Factory) { // 60 01 - push 1 // 60 00 - push 0 @@ -1293,7 +1383,7 @@ mod tests { // test is incorrect, mk // TODO: fix (preferred) or remove - evm_test_ignore!{test_transact_simple: test_transact_simple_jit, test_transact_simple_int} + evm_test_ignore!{test_transact_simple: test_transact_simple_int} fn test_transact_simple(factory: Factory) { let keypair = Random.generate().unwrap(); let t = Transaction { @@ -1331,7 +1421,7 @@ mod tests { assert_eq!(state.storage_at(&contract, &H256::new()).unwrap(), H256::from(&U256::from(1))); } - evm_test!{test_transact_invalid_nonce: test_transact_invalid_nonce_jit, test_transact_invalid_nonce_int} + evm_test!{test_transact_invalid_nonce: test_transact_invalid_nonce_int} fn test_transact_invalid_nonce(factory: Factory) { let keypair = Random.generate().unwrap(); let t = Transaction { @@ -1363,7 +1453,7 @@ mod tests { } } - evm_test!{test_transact_gas_limit_reached: test_transact_gas_limit_reached_jit, test_transact_gas_limit_reached_int} + evm_test!{test_transact_gas_limit_reached: test_transact_gas_limit_reached_int} fn test_transact_gas_limit_reached(factory: Factory) { let keypair = Random.generate().unwrap(); let t = Transaction { @@ -1396,7 +1486,7 @@ mod tests { } } - evm_test!{test_not_enough_cash: test_not_enough_cash_jit, test_not_enough_cash_int} + evm_test!{test_not_enough_cash: test_not_enough_cash_int} fn test_not_enough_cash(factory: Factory) { let keypair = Random.generate().unwrap(); @@ -1429,7 +1519,7 @@ mod tests { } } - evm_test!{test_keccak: test_keccak_jit, test_keccak_int} + evm_test!{test_keccak: test_keccak_int} fn test_keccak(factory: Factory) { let code = "6064640fffffffff20600055".from_hex().unwrap(); @@ -1461,7 +1551,7 @@ mod tests { } } - evm_test!{test_revert: test_revert_jit, test_revert_int} + evm_test!{test_revert: test_revert_int} fn test_revert(factory: Factory) { let contract_address = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); let sender = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); diff --git a/ethcore/src/externalities.rs b/ethcore/src/externalities.rs index 732d3499a83b8ebec8105467af9d1cdc77269585..65d130c3422e3702f65bf38038f86f0c33d2370e 100644 --- a/ethcore/src/externalities.rs +++ b/ethcore/src/externalities.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -414,7 +414,7 @@ mod tests { use ethereum_types::{U256, Address}; use evm::{EnvInfo, Ext, CallType}; use state::{State, Substate}; - use tests::helpers::get_temp_state; + use test_helpers::get_temp_state; use super::*; use trace::{NoopTracer, NoopVMTracer}; diff --git a/ethcore/src/factory.rs b/ethcore/src/factory.rs index 68a15f164a6eb718f75ae32062eccc70bef93712..b429073b30cc9d2219338b711c53730b76ce19a2 100644 --- a/ethcore/src/factory.rs +++ b/ethcore/src/factory.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/src/header.rs b/ethcore/src/header.rs index f6f5090ef841c3fe1eca652c044d62e1c847160b..5aa4be32375d60ffd3869ba1a493938d00bd4fa6 100644 --- a/ethcore/src/header.rs +++ b/ethcore/src/header.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -17,24 +17,36 @@ //! Block header. use std::cmp; -use std::cell::RefCell; -use std::time::{SystemTime, UNIX_EPOCH}; use hash::{KECCAK_NULL_RLP, KECCAK_EMPTY_LIST_RLP, keccak}; use heapsize::HeapSizeOf; use ethereum_types::{H256, U256, Address, Bloom}; use bytes::Bytes; -use rlp::*; +use rlp::{Rlp, RlpStream, Encodable, DecoderError, Decodable}; pub use types::BlockNumber; /// Semantic boolean for when a seal/signature is included. -pub enum Seal { +#[derive(Debug, Clone, Copy)] +enum Seal { /// The seal/signature is included. With, /// The seal/signature is not included. Without, } +/// Extended block header, wrapping `Header` with finalized and total difficulty information. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ExtendedHeader { + /// The actual header. + pub header: Header, + /// Whether the block underlying this header is considered finalized. + pub is_finalized: bool, + /// The parent block difficulty. + pub parent_total_difficulty: U256, + /// The block metadata information. + pub metadata: Option>, +} + /// A block header. /// /// Reflects the specific RLP fields of a block in the chain with additional room for the seal @@ -75,14 +87,18 @@ pub struct Header { /// Vector of post-RLP-encoded fields. seal: Vec, - /// The memoized hash of the RLP representation *including* the seal fields. - hash: RefCell>, - /// The memoized hash of the RLP representation *without* the seal fields. - bare_hash: RefCell>, + /// Memoized hash of that header and the seal. + hash: Option, } impl PartialEq for Header { fn eq(&self, c: &Header) -> bool { + if let (&Some(ref h1), &Some(ref h2)) = (&self.hash, &c.hash) { + if h1 == h2 { + return true + } + } + self.parent_hash == c.parent_hash && self.timestamp == c.timestamp && self.number == c.number && @@ -120,130 +136,178 @@ impl Default for Header { difficulty: U256::default(), seal: vec![], - hash: RefCell::new(None), - bare_hash: RefCell::new(None), + hash: None, } } } impl Header { /// Create a new, default-valued, header. - pub fn new() -> Self { - Self::default() - } + pub fn new() -> Self { Self::default() } /// Get the parent_hash field of the header. pub fn parent_hash(&self) -> &H256 { &self.parent_hash } + /// Get the timestamp field of the header. pub fn timestamp(&self) -> u64 { self.timestamp } + /// Get the number field of the header. pub fn number(&self) -> BlockNumber { self.number } + /// Get the author field of the header. pub fn author(&self) -> &Address { &self.author } /// Get the extra data field of the header. pub fn extra_data(&self) -> &Bytes { &self.extra_data } - /// Get a mutable reference to extra_data - pub fn extra_data_mut(&mut self) -> &mut Bytes { self.note_dirty(); &mut self.extra_data } /// Get the state root field of the header. pub fn state_root(&self) -> &H256 { &self.state_root } + /// Get the receipts root field of the header. pub fn receipts_root(&self) -> &H256 { &self.receipts_root } + /// Get the log bloom field of the header. pub fn log_bloom(&self) -> &Bloom { &self.log_bloom } + /// Get the transactions root field of the header. pub fn transactions_root(&self) -> &H256 { &self.transactions_root } + /// Get the uncles hash field of the header. pub fn uncles_hash(&self) -> &H256 { &self.uncles_hash } + /// Get the gas used field of the header. pub fn gas_used(&self) -> &U256 { &self.gas_used } + /// Get the gas limit field of the header. pub fn gas_limit(&self) -> &U256 { &self.gas_limit } /// Get the difficulty field of the header. pub fn difficulty(&self) -> &U256 { &self.difficulty } + /// Get the seal field of the header. pub fn seal(&self) -> &[Bytes] { &self.seal } + /// Get the seal field with RLP-decoded values as bytes. pub fn decode_seal<'a, T: ::std::iter::FromIterator<&'a [u8]>>(&'a self) -> Result { self.seal.iter().map(|rlp| { - UntrustedRlp::new(rlp).data() + Rlp::new(rlp).data() }).collect() } - // TODO: seal_at, set_seal_at &c. + /// Get a mutable reference to extra_data + #[cfg(test)] + pub fn extra_data_mut(&mut self) -> &mut Bytes { + self.hash = None; + &mut self.extra_data + } /// Set the number field of the header. - pub fn set_parent_hash(&mut self, a: H256) { self.parent_hash = a; self.note_dirty(); } + pub fn set_parent_hash(&mut self, a: H256) { + change_field(&mut self.hash, &mut self.parent_hash, a); + } + /// Set the uncles hash field of the header. - pub fn set_uncles_hash(&mut self, a: H256) { self.uncles_hash = a; self.note_dirty(); } + pub fn set_uncles_hash(&mut self, a: H256) { + change_field(&mut self.hash, &mut self.uncles_hash, a); + + } /// Set the state root field of the header. - pub fn set_state_root(&mut self, a: H256) { self.state_root = a; self.note_dirty(); } + pub fn set_state_root(&mut self, a: H256) { + change_field(&mut self.hash, &mut self.state_root, a); + } + /// Set the transactions root field of the header. - pub fn set_transactions_root(&mut self, a: H256) { self.transactions_root = a; self.note_dirty() } + pub fn set_transactions_root(&mut self, a: H256) { + change_field(&mut self.hash, &mut self.transactions_root, a); + } + /// Set the receipts root field of the header. - pub fn set_receipts_root(&mut self, a: H256) { self.receipts_root = a; self.note_dirty() } + pub fn set_receipts_root(&mut self, a: H256) { + change_field(&mut self.hash, &mut self.receipts_root, a); + } + /// Set the log bloom field of the header. - pub fn set_log_bloom(&mut self, a: Bloom) { self.log_bloom = a; self.note_dirty() } + pub fn set_log_bloom(&mut self, a: Bloom) { + change_field(&mut self.hash, &mut self.log_bloom, a); + } + /// Set the timestamp field of the header. - pub fn set_timestamp(&mut self, a: u64) { self.timestamp = a; self.note_dirty(); } - /// Set the timestamp field of the header to the current time. - pub fn set_timestamp_now(&mut self, but_later_than: u64) { let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default(); self.timestamp = cmp::max(now.as_secs() as u64, but_later_than + 1); self.note_dirty(); } + pub fn set_timestamp(&mut self, a: u64) { + change_field(&mut self.hash, &mut self.timestamp, a); + } + /// Set the number field of the header. - pub fn set_number(&mut self, a: BlockNumber) { self.number = a; self.note_dirty(); } + pub fn set_number(&mut self, a: BlockNumber) { + change_field(&mut self.hash, &mut self.number, a); + } + /// Set the author field of the header. - pub fn set_author(&mut self, a: Address) { if a != self.author { self.author = a; self.note_dirty(); } } + pub fn set_author(&mut self, a: Address) { + change_field(&mut self.hash, &mut self.author, a); + } /// Set the extra data field of the header. - pub fn set_extra_data(&mut self, a: Bytes) { if a != self.extra_data { self.extra_data = a; self.note_dirty(); } } + pub fn set_extra_data(&mut self, a: Bytes) { + change_field(&mut self.hash, &mut self.extra_data, a); + } /// Set the gas used field of the header. - pub fn set_gas_used(&mut self, a: U256) { self.gas_used = a; self.note_dirty(); } + pub fn set_gas_used(&mut self, a: U256) { + change_field(&mut self.hash, &mut self.gas_used, a); + } + /// Set the gas limit field of the header. - pub fn set_gas_limit(&mut self, a: U256) { self.gas_limit = a; self.note_dirty(); } + pub fn set_gas_limit(&mut self, a: U256) { + change_field(&mut self.hash, &mut self.gas_limit, a); + } /// Set the difficulty field of the header. - pub fn set_difficulty(&mut self, a: U256) { self.difficulty = a; self.note_dirty(); } + pub fn set_difficulty(&mut self, a: U256) { + change_field(&mut self.hash, &mut self.difficulty, a); + } + /// Set the seal field of the header. - pub fn set_seal(&mut self, a: Vec) { self.seal = a; self.note_dirty(); } + pub fn set_seal(&mut self, a: Vec) { + change_field(&mut self.hash, &mut self.seal, a) + } - /// Get the hash of this header (keccak of the RLP). + /// Get & memoize the hash of this header (keccak of the RLP with seal). + pub fn compute_hash(&mut self) -> H256 { + let hash = self.hash(); + self.hash = Some(hash); + hash + } + + /// Get the hash of this header (keccak of the RLP with seal). pub fn hash(&self) -> H256 { - let mut hash = self.hash.borrow_mut(); - match &mut *hash { - &mut Some(ref h) => h.clone(), - hash @ &mut None => { - let h = self.rlp_keccak(Seal::With); - *hash = Some(h.clone()); - h - } - } + self.hash.unwrap_or_else(|| keccak(self.rlp(Seal::With))) } /// Get the hash of the header excluding the seal pub fn bare_hash(&self) -> H256 { - let mut hash = self.bare_hash.borrow_mut(); - match &mut *hash { - &mut Some(ref h) => h.clone(), - hash @ &mut None => { - let h = self.rlp_keccak(Seal::Without); - *hash = Some(h.clone()); - h - } - } + keccak(self.rlp(Seal::Without)) } - /// Note that some fields have changed. Resets the memoised hash. - pub fn note_dirty(&self) { - *self.hash.borrow_mut() = None; - *self.bare_hash.borrow_mut() = None; + /// Encode the header, getting a type-safe wrapper around the RLP. + pub fn encoded(&self) -> ::encoded::Header { + ::encoded::Header::new(self.rlp(Seal::With)) + } + + /// Get the RLP representation of this Header. + fn rlp(&self, with_seal: Seal) -> Bytes { + let mut s = RlpStream::new(); + self.stream_rlp(&mut s, with_seal); + s.out() } - // TODO: make these functions traity /// Place this header into an RLP stream `s`, optionally `with_seal`. - pub fn stream_rlp(&self, s: &mut RlpStream, with_seal: Seal) { - s.begin_list(13 + match with_seal { Seal::With => self.seal.len(), _ => 0 }); + fn stream_rlp(&self, s: &mut RlpStream, with_seal: Seal) { + if let Seal::With = with_seal { + s.begin_list(13 + self.seal.len()); + } else { + s.begin_list(13); + } + s.append(&self.parent_hash); s.append(&self.uncles_hash); s.append(&self.author); @@ -257,31 +321,25 @@ impl Header { s.append(&self.gas_used); s.append(&self.timestamp); s.append(&self.extra_data); + if let Seal::With = with_seal { for b in &self.seal { s.append_raw(b, 1); } } } +} - /// Get the RLP of this header, optionally `with_seal`. - pub fn rlp(&self, with_seal: Seal) -> Bytes { - let mut s = RlpStream::new(); - self.stream_rlp(&mut s, with_seal); - s.out() - } - - /// Get the SHA3 (Keccak) of this header, optionally `with_seal`. - pub fn rlp_keccak(&self, with_seal: Seal) -> H256 { keccak(self.rlp(with_seal)) } - - /// Encode the header, getting a type-safe wrapper around the RLP. - pub fn encoded(&self) -> ::encoded::Header { - ::encoded::Header::new(self.rlp(Seal::With)) +/// Alter value of given field, reset memoised hash if changed. +fn change_field(hash: &mut Option, field: &mut T, value: T) where T: PartialEq { + if field != &value { + *field = value; + *hash = None; } } impl Decodable for Header { - fn decode(r: &UntrustedRlp) -> Result { + fn decode(r: &Rlp) -> Result { let mut blockheader = Header { parent_hash: r.val_at(0)?, uncles_hash: r.val_at(1)?, @@ -297,8 +355,7 @@ impl Decodable for Header { timestamp: cmp::min(r.val_at::(11)?, u64::max_value().into()).as_u64(), extra_data: r.val_at(12)?, seal: vec![], - hash: RefCell::new(Some(keccak(r.as_raw()))), - bare_hash: RefCell::new(None), + hash: keccak(r.as_raw()).into(), }; for i in 13..r.item_count()? { @@ -323,21 +380,48 @@ impl HeapSizeOf for Header { impl ::parity_machine::Header for Header { fn bare_hash(&self) -> H256 { Header::bare_hash(self) } - fn hash(&self) -> H256 { Header::hash(self) } - fn seal(&self) -> &[Vec] { Header::seal(self) } - fn author(&self) -> &Address { Header::author(self) } - fn number(&self) -> BlockNumber { Header::number(self) } } impl ::parity_machine::ScoredHeader for Header { + type Value = U256; + fn score(&self) -> &U256 { self.difficulty() } fn set_score(&mut self, score: U256) { self.set_difficulty(score) } } +impl ::parity_machine::Header for ExtendedHeader { + fn bare_hash(&self) -> H256 { self.header.bare_hash() } + fn hash(&self) -> H256 { self.header.hash() } + fn seal(&self) -> &[Vec] { self.header.seal() } + fn author(&self) -> &Address { self.header.author() } + fn number(&self) -> BlockNumber { self.header.number() } +} + +impl ::parity_machine::ScoredHeader for ExtendedHeader { + type Value = U256; + + fn score(&self) -> &U256 { self.header.difficulty() } + fn set_score(&mut self, score: U256) { self.header.set_difficulty(score) } +} + +impl ::parity_machine::TotalScoredHeader for ExtendedHeader { + type Value = U256; + + fn total_score(&self) -> U256 { self.parent_total_difficulty + *self.header.difficulty() } +} + +impl ::parity_machine::FinalizableHeader for ExtendedHeader { + fn is_finalized(&self) -> bool { self.is_finalized } +} + +impl ::parity_machine::WithMetadataHeader for ExtendedHeader { + fn metadata(&self) -> Option<&[u8]> { self.metadata.as_ref().map(|v| v.as_ref()) } +} + #[cfg(test)] mod tests { use rustc_hex::FromHex; @@ -353,7 +437,7 @@ mod tests { let nonce = "88ab4e252a7e8c2a23".from_hex().unwrap(); let nonce_decoded = "ab4e252a7e8c2a23".from_hex().unwrap(); - let header: Header = rlp::decode(&header_rlp); + let header: Header = rlp::decode(&header_rlp).expect("error decoding header"); let seal_fields = header.seal.clone(); assert_eq!(seal_fields.len(), 2); assert_eq!(seal_fields[0], mix_hash); @@ -370,7 +454,7 @@ mod tests { // that's rlp of block header created with ethash engine. let header_rlp = "f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d84568e932a80a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23".from_hex().unwrap(); - let header: Header = rlp::decode(&header_rlp); + let header: Header = rlp::decode(&header_rlp).expect("error decoding header"); let encoded_header = rlp::encode(&header).into_vec(); assert_eq!(header_rlp, encoded_header); diff --git a/ethcore/src/json_tests/chain.rs b/ethcore/src/json_tests/chain.rs index 64414450bd5cb1babc242e94ecfd94fbd6dbc2e9..814538cdbd347251062d773ea19406cb8fa93fec 100644 --- a/ethcore/src/json_tests/chain.rs +++ b/ethcore/src/json_tests/chain.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -64,7 +64,7 @@ pub fn json_chain_test(json_data: &[u8]) -> Vec { config, &spec, db, - Arc::new(Miner::with_spec(&spec)), + Arc::new(Miner::new_for_tests(&spec, None)), IoChannel::disconnected(), ).unwrap(); for b in &blockchain.blocks_rlp() { @@ -152,4 +152,3 @@ mod block_tests { declare_test!{BlockchainTests_TransitionTests_bcHomesteadToDao, "BlockchainTests/TransitionTests/bcHomesteadToDao/"} declare_test!{BlockchainTests_TransitionTests_bcHomesteadToEIP150, "BlockchainTests/TransitionTests/bcHomesteadToEIP150/"} } - diff --git a/ethcore/src/json_tests/difficulty.rs b/ethcore/src/json_tests/difficulty.rs index c0d03c810b467ef3fcd696800787274042d899ee..d111f0890dbacc986e76c49cfe0c147d49c342a3 100644 --- a/ethcore/src/json_tests/difficulty.rs +++ b/ethcore/src/json_tests/difficulty.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -56,7 +56,6 @@ mod difficulty_test_byzantium { declare_test!{DifficultyTests_difficultyByzantium, "BasicTests/difficultyByzantium.json"} } - mod difficulty_test_foundation { use super::json_difficulty_test; use tempdir::TempDir; @@ -68,6 +67,3 @@ mod difficulty_test_foundation { declare_test!{DifficultyTests_difficultyMainNetwork, "BasicTests/difficultyMainNetwork.json"} } - - - diff --git a/ethcore/src/json_tests/executive.rs b/ethcore/src/json_tests/executive.rs index 0ec60d49319f22e05f5b7f30108236280f9f66d0..5bda6c55a3975946982a578fba3ae8651c07b5ee 100644 --- a/ethcore/src/json_tests/executive.rs +++ b/ethcore/src/json_tests/executive.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -25,7 +25,7 @@ use vm::{ CreateContractAddress, ReturnData, }; use externalities::*; -use tests::helpers::get_temp_state; +use test_helpers::get_temp_state; use ethjson; use trace::{Tracer, NoopTracer}; use trace::{VMTracer, NoopVMTracer}; diff --git a/ethcore/src/json_tests/mod.rs b/ethcore/src/json_tests/mod.rs index a0966a2d29e8856196deb60a3d41332698d91e6b..65cc6d2134076abb07c2413e6ef73ef3f53a2ae0 100644 --- a/ethcore/src/json_tests/mod.rs +++ b/ethcore/src/json_tests/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/src/json_tests/state.rs b/ethcore/src/json_tests/state.rs index a55ab18443dad30fe20d5c56d6c58fd7fee8d224..45ec6f3fb0c91cbb2cb717ce553b1730f8f7b89d 100644 --- a/ethcore/src/json_tests/state.rs +++ b/ethcore/src/json_tests/state.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -135,4 +135,3 @@ mod state_tests { declare_test!{GeneralStateTest_stZeroCallsTest, "GeneralStateTests/stZeroCallsTest/"} declare_test!{GeneralStateTest_stZeroKnowledge, "GeneralStateTests/stZeroKnowledge/"} } - diff --git a/ethcore/src/json_tests/test_common.rs b/ethcore/src/json_tests/test_common.rs index 83f6b55272d5e87e2ca6afdff3dc56e500f735a2..6ce38b27a0cd35bc63586d2a62fbfeb654314f76 100644 --- a/ethcore/src/json_tests/test_common.rs +++ b/ethcore/src/json_tests/test_common.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/src/json_tests/transaction.rs b/ethcore/src/json_tests/transaction.rs index b7445c51ad3adca47889535008ddc08aceb0c019..295093305d7d00311e1207d6b2494e2c6bf17622 100644 --- a/ethcore/src/json_tests/transaction.rs +++ b/ethcore/src/json_tests/transaction.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -17,7 +17,7 @@ use super::test_common::*; use evm; use ethjson; -use rlp::UntrustedRlp; +use rlp::Rlp; use transaction::{Action, UnverifiedTransaction, SignedTransaction}; fn do_json_test(json_data: &[u8]) -> Vec { @@ -40,7 +40,7 @@ fn do_json_test(json_data: &[u8]) -> Vec { let allow_unsigned = number.map_or(false, |n| n >= 3_000_000); let rlp: Vec = test.rlp.into(); - let res = UntrustedRlp::new(&rlp) + let res = Rlp::new(&rlp) .as_val() .map_err(::error::Error::from) .and_then(|t: UnverifiedTransaction| { diff --git a/ethcore/src/json_tests/trie.rs b/ethcore/src/json_tests/trie.rs index f5803d2d372e5274f0833c6bde0e6977ee8275a2..fae7cc7380a230448dc5d23917d7d0604f94dcf5 100644 --- a/ethcore/src/json_tests/trie.rs +++ b/ethcore/src/json_tests/trie.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index 343cef9f4165760cbb6e6490d33d8c4e5f3abcb5..51e75d4b40a79b6396d6fd738f7222f797724625 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -15,7 +15,7 @@ // along with Parity. If not, see . #![warn(missing_docs)] -#![cfg_attr(feature="benches", feature(test))] +#![cfg_attr(feature = "benches", feature(test))] //! Ethcore library //! @@ -54,14 +54,18 @@ //! cargo build --release //! ``` +// Recursion limit required because of +// error_chain foreign_links. +#![recursion_limit="128"] + extern crate bloomchain; extern crate bn; extern crate byteorder; extern crate crossbeam; extern crate common_types as types; -extern crate crypto; extern crate ethash; extern crate ethcore_bloom_journal as bloom_journal; +extern crate ethcore_crypto; extern crate ethcore_io as io; extern crate ethcore_bytes as bytes; extern crate ethcore_logger; @@ -71,7 +75,6 @@ extern crate ethcore_transaction as transaction; extern crate ethereum_types; extern crate ethjson; extern crate ethkey; -extern crate futures_cpupool; extern crate hardware_wallet; extern crate hashdb; extern crate itertools; @@ -80,7 +83,6 @@ extern crate num_cpus; extern crate num; extern crate parity_machine; extern crate parking_lot; -extern crate price_info; extern crate rand; extern crate rayon; extern crate rlp; @@ -93,25 +95,15 @@ extern crate triehash; extern crate ansi_term; extern crate unexpected; extern crate kvdb; -extern crate kvdb_rocksdb; extern crate kvdb_memorydb; extern crate util_error; extern crate snappy; -extern crate migration; extern crate ethabi; -#[macro_use] -extern crate ethabi_derive; -#[macro_use] -extern crate ethabi_contract; - -#[macro_use] -extern crate rlp_derive; extern crate rustc_hex; extern crate stats; extern crate stop_guard; extern crate using_queue; -extern crate table; extern crate vm; extern crate wasm; extern crate memory_cache; @@ -120,21 +112,33 @@ extern crate journaldb; extern crate tempdir; #[macro_use] -extern crate macros; +extern crate ethabi_derive; +#[macro_use] +extern crate ethabi_contract; +#[macro_use] +extern crate error_chain; #[macro_use] extern crate log; #[macro_use] extern crate lazy_static; #[macro_use] +extern crate macros; +#[macro_use] +extern crate rlp_derive; +#[macro_use] extern crate trace_time; + #[cfg_attr(test, macro_use)] extern crate evm; -#[cfg(feature = "jit" )] -extern crate evmjit; - pub extern crate ethstore; +#[macro_use] +pub mod views; + +#[cfg(test)] +extern crate kvdb_rocksdb; + pub mod account_provider; pub mod block; pub mod client; @@ -144,6 +148,7 @@ pub mod engines; pub mod error; pub mod ethereum; pub mod executed; +pub mod executive; pub mod header; pub mod machine; pub mod miner; @@ -154,14 +159,12 @@ pub mod state; pub mod state_db; pub mod trace; pub mod verification; -pub mod views; mod cache_manager; mod blooms; mod pod_account; mod account_db; mod builtin; -mod executive; mod externalities; mod blockchain; mod factory; @@ -170,8 +173,12 @@ mod tx_filter; #[cfg(test)] mod tests; #[cfg(test)] -#[cfg(feature="json-tests")] +#[cfg(feature = "json-tests")] mod json_tests; +#[cfg(any(test, feature = "test-helpers"))] +pub mod test_helpers; +#[cfg(test)] +mod test_helpers_internal; pub use types::*; pub use executive::contract_address; diff --git a/ethcore/src/machine.rs b/ethcore/src/machine.rs index ad36aa69d795736fd81aa351c28d436b496fc9b7..dbf66aa1217ee422ebb33fba39d53c990b8005e4 100644 --- a/ethcore/src/machine.rs +++ b/ethcore/src/machine.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -25,7 +25,7 @@ use builtin::Builtin; use client::{BlockInfo, CallContract}; use error::Error; use executive::Executive; -use header::{BlockNumber, Header}; +use header::{BlockNumber, Header, ExtendedHeader}; use spec::CommonParams; use state::{CleanupMode, Substate}; use trace::{NoopTracer, NoopVMTracer, Tracer, ExecutiveTracer, RewardType, Tracing}; @@ -34,6 +34,7 @@ use tx_filter::TransactionFilter; use ethereum_types::{U256, Address}; use bytes::BytesRef; +use rlp::Rlp; use vm::{CallType, ActionParams, ActionValue, ParamsType}; use vm::{EnvInfo, Schedule, CreateContractAddress}; @@ -45,14 +46,6 @@ pub const PARITY_GAS_LIMIT_DETERMINANT: U256 = U256([37, 0, 0, 0]); pub struct EthashExtensions { /// Homestead transition block number. pub homestead_transition: BlockNumber, - /// EIP150 transition block number. - pub eip150_transition: BlockNumber, - /// Number of first block where EIP-160 rules begin. - pub eip160_transition: u64, - /// Number of first block where EIP-161.abc begin. - pub eip161abc_transition: u64, - /// Number of first block where EIP-161.d begins. - pub eip161d_transition: u64, /// DAO hard-fork transition block (X). pub dao_hardfork_transition: u64, /// DAO hard-fork refund contract address (C). @@ -65,10 +58,6 @@ impl From<::ethjson::spec::EthashParams> for EthashExtensions { fn from(p: ::ethjson::spec::EthashParams) -> Self { EthashExtensions { homestead_transition: p.homestead_transition.map_or(0, Into::into), - eip150_transition: p.eip150_transition.map_or(0, Into::into), - eip160_transition: p.eip160_transition.map_or(0, Into::into), - eip161abc_transition: p.eip161abc_transition.map_or(0, Into::into), - eip161d_transition: p.eip161d_transition.map_or(u64::max_value(), Into::into), dao_hardfork_transition: p.dao_hardfork_transition.map_or(u64::max_value(), Into::into), dao_hardfork_beneficiary: p.dao_hardfork_beneficiary.map_or_else(Address::new, Into::into), dao_hardfork_accounts: p.dao_hardfork_accounts.unwrap_or_else(Vec::new).into_iter().map(Into::into).collect(), @@ -121,7 +110,13 @@ impl EthereumMachine { } impl EthereumMachine { - /// Execute a call as the system address. + /// Execute a call as the system address. Block environment information passed to the + /// VM is modified to have its gas limit bounded at the upper limit of possible used + /// gases including this system call, capped at the maximum value able to be + /// represented by U256. This system call modifies the block state, but discards other + /// information. If suicides, logs or refunds happen within the system call, they + /// will not be executed or recorded. Gas used by this system call will not be counted + /// on the block. pub fn execute_as_system( &self, block: &mut ExecutedBlock, @@ -131,7 +126,7 @@ impl EthereumMachine { ) -> Result, Error> { let env_info = { let mut env_info = block.env_info(); - env_info.gas_limit = env_info.gas_used + gas; + env_info.gas_limit = env_info.gas_used.saturating_add(gas); env_info }; @@ -203,10 +198,11 @@ impl EthereumMachine { /// The gas floor target must not be lower than the engine's minimum gas limit. pub fn populate_from_parent(&self, header: &mut Header, parent: &Header, gas_floor_target: U256, gas_ceil_target: U256) { header.set_difficulty(parent.difficulty().clone()); + let gas_limit = parent.gas_limit().clone(); + assert!(!gas_limit.is_zero(), "Gas limit should be > 0"); if let Some(ref ethash_params) = self.ethash_extensions { let gas_limit = { - let gas_limit = parent.gas_limit().clone(); let bound_divisor = self.params().gas_limit_bound_divisor; let lower_limit = gas_limit - gas_limit / bound_divisor + 1.into(); let upper_limit = gas_limit + gas_limit / bound_divisor - 1.into(); @@ -238,7 +234,6 @@ impl EthereumMachine { } header.set_gas_limit({ - let gas_limit = parent.gas_limit().clone(); let bound_divisor = self.params().gas_limit_bound_divisor; if gas_limit < gas_floor_target { cmp::min(gas_floor_target, gas_limit + gas_limit / bound_divisor - 1.into()) @@ -260,19 +255,8 @@ impl EthereumMachine { Some(ref ext) => { if block_number < ext.homestead_transition { Schedule::new_frontier() - } else if block_number < ext.eip150_transition { - Schedule::new_homestead() } else { - let max_code_size = self.params.max_code_size(block_number); - let mut schedule = Schedule::new_post_eip150( - max_code_size as _, - block_number >= ext.eip160_transition, - block_number >= ext.eip161abc_transition, - block_number >= ext.eip161d_transition - ); - - self.params.update_schedule(block_number, &mut schedule); - schedule + self.params.schedule(block_number) } } }; @@ -334,12 +318,12 @@ impl EthereumMachine { } /// Verify a particular transaction is valid, regardless of order. - pub fn verify_transaction_unordered(&self, t: UnverifiedTransaction, _header: &Header) -> Result { + pub fn verify_transaction_unordered(&self, t: UnverifiedTransaction, _header: &Header) -> Result { Ok(SignedTransaction::new(t)?) } /// Does basic verification of the transaction. - pub fn verify_transaction_basic(&self, t: &UnverifiedTransaction, header: &Header) -> Result<(), Error> { + pub fn verify_transaction_basic(&self, t: &UnverifiedTransaction, header: &Header) -> Result<(), transaction::Error> { let check_low_s = match self.ethash_extensions { Some(ref ext) => header.number() >= ext.homestead_transition, None => true, @@ -358,9 +342,9 @@ impl EthereumMachine { } /// Does verification of the transaction against the parent state. - // TODO: refine the bound here to be a "state provider" or similar as opposed - // to full client functionality. - pub fn verify_transaction(&self, t: &SignedTransaction, header: &Header, client: &C) -> Result<(), Error> { + pub fn verify_transaction(&self, t: &SignedTransaction, header: &Header, client: &C) + -> Result<(), transaction::Error> + { if let Some(ref filter) = self.tx_filter.as_ref() { if !filter.transaction_allowed(header.parent_hash(), t, client) { return Err(transaction::Error::NotAllowed.into()) @@ -376,6 +360,16 @@ impl EthereumMachine { "registrar".to_owned() => format!("{:x}", self.params.registrar) ] } + + /// Performs pre-validation of RLP decoded transaction before other processing + pub fn decode_transaction(&self, transaction: &[u8]) -> Result { + let rlp = Rlp::new(&transaction); + if rlp.as_raw().len() > self.params().max_transaction_size { + debug!("Rejected oversized transaction of {} bytes", rlp.as_raw().len()); + return Err(transaction::Error::TooBig) + } + rlp.as_val().map_err(|e| transaction::Error::InvalidRlp(e.to_string())) + } } /// Auxiliary data fetcher for an Ethereum machine. In Ethereum-like machines @@ -405,10 +399,12 @@ pub enum AuxiliaryRequest { impl ::parity_machine::Machine for EthereumMachine { type Header = Header; + type ExtendedHeader = ExtendedHeader; type LiveBlock = ExecutedBlock; type EngineClient = ::client::EngineClient; type AuxiliaryRequest = AuxiliaryRequest; + type AncestryAction = ::types::ancestry_action::AncestryAction; type Error = Error; } @@ -426,22 +422,30 @@ impl ::parity_machine::WithBalances for EthereumMachine { fn add_balance(&self, live: &mut ExecutedBlock, address: &Address, amount: &U256) -> Result<(), Error> { live.state_mut().add_balance(address, amount, CleanupMode::NoEmpty).map_err(Into::into) } +} + +/// A state machine that uses block rewards. +pub trait WithRewards: ::parity_machine::Machine { + /// Note block rewards, traces each reward storing information about benefactor, amount and type + /// of reward. + fn note_rewards( + &self, + live: &mut Self::LiveBlock, + rewards: &[(Address, RewardType, U256)], + ) -> Result<(), Self::Error>; +} +impl WithRewards for EthereumMachine { fn note_rewards( &self, live: &mut Self::LiveBlock, - direct: &[(Address, U256)], - indirect: &[(Address, U256)], + rewards: &[(Address, RewardType, U256)], ) -> Result<(), Self::Error> { if let Tracing::Enabled(ref mut traces) = *live.traces_mut() { let mut tracer = ExecutiveTracer::default(); - for &(address, amount) in direct { - tracer.trace_reward(address, amount, RewardType::Block); - } - - for &(address, amount) in indirect { - tracer.trace_reward(address, amount, RewardType::Uncle); + for &(address, ref reward_type, amount) in rewards { + tracer.trace_reward(address, amount, reward_type.clone()); } traces.push(tracer.drain().into()); @@ -468,7 +472,6 @@ fn round_block_gas_limit(gas_limit: U256, lower_limit: U256, upper_limit: U256) } } - #[cfg(test)] mod tests { use super::*; @@ -476,16 +479,31 @@ mod tests { fn get_default_ethash_extensions() -> EthashExtensions { EthashExtensions { homestead_transition: 1150000, - eip150_transition: u64::max_value(), - eip160_transition: u64::max_value(), - eip161abc_transition: u64::max_value(), - eip161d_transition: u64::max_value(), dao_hardfork_transition: u64::max_value(), dao_hardfork_beneficiary: "0000000000000000000000000000000000000001".into(), dao_hardfork_accounts: Vec::new(), } } + #[test] + fn should_disallow_unsigned_transactions() { + let rlp = "ea80843b9aca0083015f90948921ebb5f79e9e3920abe571004d0b1d5119c154865af3107a400080038080".into(); + let transaction: UnverifiedTransaction = ::rlp::decode(&::rustc_hex::FromHex::from_hex(rlp).unwrap()).unwrap(); + let spec = ::ethereum::new_ropsten_test(); + let ethparams = get_default_ethash_extensions(); + + let machine = EthereumMachine::with_ethash_extensions( + spec.params().clone(), + Default::default(), + ethparams, + ); + let mut header = ::header::Header::new(); + header.set_number(15); + + let res = machine.verify_transaction_basic(&transaction, &header); + assert_eq!(res, Err(transaction::Error::InvalidSignature("Crypto error (Invalid EC signature)".into()))); + } + #[test] fn ethash_gas_limit_is_multiple_of_determinant() { use ethereum_types::U256; diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index 58111cd741ad70c109f16593a7d87d8793cd32be..af03b2d1e0a1a9267338d6515cffa56dc6ce8743 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -15,49 +15,38 @@ // along with Parity. If not, see . use std::time::{Instant, Duration}; -use std::collections::{BTreeMap, HashSet}; +use std::collections::{BTreeMap, HashSet, HashMap}; use std::sync::Arc; -use account_provider::{AccountProvider, SignError as AccountError}; use ansi_term::Colour; -use ethereum_types::{H256, U256, Address}; -use parking_lot::{Mutex, RwLock}; use bytes::Bytes; use engines::{EthEngine, Seal}; -use error::*; -use ethcore_miner::banning_queue::{BanningTransactionQueue, Threshold}; -use ethcore_miner::local_transactions::{Status as LocalTransactionStatus}; -use ethcore_miner::transaction_queue::{ - TransactionQueue, - RemovalReason, - TransactionDetailsProvider as TransactionQueueDetailsProvider, - PrioritizationStrategy, - AccountDetails, - TransactionOrigin, -}; -use futures_cpupool::CpuPool; -use ethcore_miner::work_notify::{WorkPoster, NotifyWork}; -use miner::service_transaction_checker::ServiceTransactionChecker; -use miner::{MinerService, MinerStatus}; -use price_info::fetch::Client as FetchClient; -use price_info::{Client as PriceInfoClient, PriceInfo}; +use error::{Error, ErrorKind, ExecutionError}; +use ethcore_miner::gas_pricer::GasPricer; +use ethcore_miner::pool::{self, TransactionQueue, VerifiedTransaction, QueueStatus, PrioritizationStrategy}; +use ethcore_miner::work_notify::NotifyWork; +use ethereum_types::{H256, U256, Address}; +use parking_lot::{Mutex, RwLock}; +use rayon::prelude::*; use transaction::{ + self, Action, UnverifiedTransaction, - PendingTransaction, SignedTransaction, - Condition as TransactionCondition, - ImportResult as TransactionImportResult, - Error as TransactionError, + PendingTransaction, }; use using_queue::{UsingQueue, GetAction}; -use block::{ClosedBlock, IsBlock, Block}; + +use account_provider::{AccountProvider, SignError as AccountError}; +use block::{ClosedBlock, IsBlock, Block, SealedBlock}; use client::{ - AccountData, BlockChain, RegistryInfo, ScheduleInfo, CallContract, BlockProducer, SealedBlockImporter + BlockChain, ChainInfo, CallContract, BlockProducer, SealedBlockImporter, Nonce }; -use client::{BlockId, TransactionId, MiningBlockChainClient}; +use client::BlockId; use executive::contract_address; use header::{Header, BlockNumber}; +use miner; +use miner::pool_client::{PoolClient, CachedNonceClient}; use receipt::{Receipt, RichReceipt}; use spec::Spec; use state::State; @@ -68,44 +57,47 @@ pub enum PendingSet { /// Always just the transactions in the queue. These have had only cheap checks. AlwaysQueue, /// Always just the transactions in the sealing block. These have had full checks but - /// may be empty if the node is not actively mining or has force_sealing enabled. + /// may be empty if the node is not actively mining or has no force_sealing enabled. AlwaysSealing, - /// Try the sealing block, but if it is not currently sealing, fallback to the queue. + /// Takes from sealing if mining, from queue otherwise. SealingOrElseQueue, } -/// Type of the gas limit to apply to the transaction queue. -#[derive(Debug, PartialEq)] -pub enum GasLimit { - /// Depends on the block gas limit and is updated with every block. - Auto, - /// No limit. - None, - /// Set to a fixed gas value. - Fixed(U256), -} - -/// Transaction queue banning settings. +/// Transaction queue penalization settings. +/// +/// Senders of long-running transactions (above defined threshold) +/// will get lower priority. #[derive(Debug, PartialEq, Clone)] -pub enum Banning { - /// Banning in transaction queue is disabled +pub enum Penalization { + /// Penalization in transaction queue is disabled Disabled, - /// Banning in transaction queue is enabled + /// Penalization in transaction queue is enabled Enabled { - /// Upper limit of transaction processing time before banning. + /// Upper limit of transaction processing time before penalizing. offend_threshold: Duration, - /// Number of similar offending transactions before banning. - min_offends: u16, - /// Number of seconds the offender is banned for. - ban_duration: Duration, }, } +/// Initial minimal gas price. +/// +/// Gas price should be later overwritten externally +/// for instance by a dynamic gas price mechanism or CLI parameter. +/// This constant controls the initial value. +const DEFAULT_MINIMAL_GAS_PRICE: u64 = 20_000_000_000; + +/// Allowed number of skipped transactions when constructing pending block. +/// +/// When we push transactions to pending block, some of the transactions might +/// get skipped because of block gas limit being reached. +/// This constant controls how many transactions we can skip because of that +/// before stopping attempts to push more transactions to the block. +/// This is an optimization that prevents traversing the entire pool +/// in case we have only a fraction of available block gas limit left. +const MAX_SKIPPED_TRANSACTIONS: usize = 8; + /// Configures the behaviour of the miner. #[derive(Debug, PartialEq)] pub struct MinerOptions { - /// URLs to notify when there is new work. - pub new_work_notify: Vec, /// Force the miner to reseal, even when nobody has asked for work. pub force_sealing: bool, /// Reseal on receipt of new external transactions. @@ -118,287 +110,220 @@ pub struct MinerOptions { pub reseal_min_period: Duration, /// Maximum period between blocks (enables force sealing after that). pub reseal_max_period: Duration, - /// Maximum amount of gas to bother considering for block insertion. - pub tx_gas_limit: U256, - /// Maximum size of the transaction queue. - pub tx_queue_size: usize, - /// Maximum memory usage of transactions in the queue (current / future). - pub tx_queue_memory_limit: Option, - /// Strategy to use for prioritizing transactions in the queue. - pub tx_queue_strategy: PrioritizationStrategy, /// Whether we should fallback to providing all the queue's transactions or just pending. pub pending_set: PendingSet, /// How many historical work packages can we store before running out? pub work_queue_size: usize, /// Can we submit two different solutions for the same block and expect both to result in an import? pub enable_resubmission: bool, - /// Global gas limit for all transaction in the queue except for local and retracted. - pub tx_queue_gas_limit: GasLimit, - /// Banning settings. - pub tx_queue_banning: Banning, - /// Do we refuse to accept service transactions even if sender is certified. - pub refuse_service_transactions: bool, /// Create a pending block with maximal possible gas limit. /// NOTE: Such block will contain all pending transactions but /// will be invalid if mined. pub infinite_pending_block: bool, + + /// Strategy to use for prioritizing transactions in the queue. + pub tx_queue_strategy: PrioritizationStrategy, + /// Simple senders penalization. + pub tx_queue_penalization: Penalization, + /// Do we want to mark transactions recieved locally (e.g. RPC) as local if we don't have the sending account? + pub tx_queue_no_unfamiliar_locals: bool, + /// Do we refuse to accept service transactions even if sender is certified. + pub refuse_service_transactions: bool, + /// Transaction pool limits. + pub pool_limits: pool::Options, + /// Initial transaction verification options. + pub pool_verification_options: pool::verifier::Options, } impl Default for MinerOptions { fn default() -> Self { MinerOptions { - new_work_notify: vec![], force_sealing: false, reseal_on_external_tx: false, reseal_on_own_tx: true, reseal_on_uncle: false, - tx_gas_limit: !U256::zero(), - tx_queue_size: 8192, - tx_queue_memory_limit: Some(2 * 1024 * 1024), - tx_queue_gas_limit: GasLimit::None, - tx_queue_strategy: PrioritizationStrategy::GasPriceOnly, - pending_set: PendingSet::AlwaysQueue, reseal_min_period: Duration::from_secs(2), reseal_max_period: Duration::from_secs(120), + pending_set: PendingSet::AlwaysQueue, work_queue_size: 20, enable_resubmission: true, - tx_queue_banning: Banning::Disabled, - refuse_service_transactions: false, infinite_pending_block: false, + tx_queue_strategy: PrioritizationStrategy::GasPriceOnly, + tx_queue_penalization: Penalization::Disabled, + tx_queue_no_unfamiliar_locals: false, + refuse_service_transactions: false, + pool_limits: pool::Options { + max_count: 8_192, + max_per_sender: 81, + max_mem_usage: 4 * 1024 * 1024, + }, + pool_verification_options: pool::verifier::Options { + minimal_gas_price: DEFAULT_MINIMAL_GAS_PRICE.into(), + block_gas_limit: U256::max_value(), + tx_gas_limit: U256::max_value(), + }, } } } -/// Options for the dynamic gas price recalibrator. -#[derive(Debug, PartialEq)] -pub struct GasPriceCalibratorOptions { - /// Base transaction price to match against. - pub usd_per_tx: f32, - /// How frequently we should recalibrate. - pub recalibration_period: Duration, -} - -/// The gas price validator variant for a `GasPricer`. -#[derive(Debug, PartialEq)] -pub struct GasPriceCalibrator { - options: GasPriceCalibratorOptions, - next_calibration: Instant, - price_info: PriceInfoClient, -} - -impl GasPriceCalibrator { - fn recalibrate(&mut self, set_price: F) { - trace!(target: "miner", "Recalibrating {:?} versus {:?}", Instant::now(), self.next_calibration); - if Instant::now() >= self.next_calibration { - let usd_per_tx = self.options.usd_per_tx; - trace!(target: "miner", "Getting price info"); - - self.price_info.get(move |price: PriceInfo| { - trace!(target: "miner", "Price info arrived: {:?}", price); - let usd_per_eth = price.ethusd; - let wei_per_usd: f32 = 1.0e18 / usd_per_eth; - let gas_per_tx: f32 = 21000.0; - let wei_per_gas: f32 = wei_per_usd * usd_per_tx / gas_per_tx; - info!(target: "miner", "Updated conversion rate to Ξ1 = {} ({} wei/gas)", Colour::White.bold().paint(format!("US${:.2}", usd_per_eth)), Colour::Yellow.bold().paint(format!("{}", wei_per_gas))); - set_price(U256::from(wei_per_gas as u64)); - }); - - self.next_calibration = Instant::now() + self.options.recalibration_period; - } - } -} - -/// Struct to look after updating the acceptable gas price of a miner. -#[derive(Debug, PartialEq)] -pub enum GasPricer { - /// A fixed gas price in terms of Wei - always the argument given. - Fixed(U256), - /// Gas price is calibrated according to a fixed amount of USD. - Calibrated(GasPriceCalibrator), -} - -impl GasPricer { - /// Create a new Calibrated `GasPricer`. - pub fn new_calibrated(options: GasPriceCalibratorOptions, fetch: FetchClient, p: CpuPool) -> GasPricer { - GasPricer::Calibrated(GasPriceCalibrator { - options: options, - next_calibration: Instant::now(), - price_info: PriceInfoClient::new(fetch, p), - }) - } - - /// Create a new Fixed `GasPricer`. - pub fn new_fixed(gas_price: U256) -> GasPricer { - GasPricer::Fixed(gas_price) - } - - fn recalibrate(&mut self, set_price: F) { - match *self { - GasPricer::Fixed(ref max) => set_price(max.clone()), - GasPricer::Calibrated(ref mut cal) => cal.recalibrate(set_price), - } - } +/// Configurable parameters of block authoring. +#[derive(Debug, Default, Clone)] +pub struct AuthoringParams { + /// Lower and upper bound of block gas limit that we are targeting + pub gas_range_target: (U256, U256), + /// Block author + pub author: Address, + /// Block extra data + pub extra_data: Bytes, } struct SealingWork { queue: UsingQueue, enabled: bool, + next_allowed_reseal: Instant, + next_mandatory_reseal: Instant, + // block number when sealing work was last requested + last_request: Option, +} + +impl SealingWork { + /// Are we allowed to do a non-mandatory reseal? + fn reseal_allowed(&self) -> bool { + Instant::now() > self.next_allowed_reseal + } } /// Keeps track of transactions using priority queue and holds currently mined block. /// Handles preparing work for "work sealing" or seals "internally" if Engine does not require work. pub struct Miner { // NOTE [ToDr] When locking always lock in this order! - transaction_queue: Arc>, - transaction_listener: RwLock>>, - sealing_work: Mutex, - next_allowed_reseal: Mutex, - next_mandatory_reseal: RwLock, - sealing_block_last_request: Mutex, - // for sealing... + sealing: Mutex, + params: RwLock, + listeners: RwLock>>, + nonce_cache: RwLock>, + gas_pricer: Mutex, options: MinerOptions, - - gas_range_target: RwLock<(U256, U256)>, - author: RwLock
, - extra_data: RwLock, + // TODO [ToDr] Arc is only required because of price updater + transaction_queue: Arc, engine: Arc, - accounts: Option>, - notifiers: RwLock>>, - gas_pricer: Mutex, - service_transaction_action: ServiceTransactionAction, } impl Miner { - /// Push notifier that will handle new jobs - pub fn push_notifier(&self, notifier: Box) { - self.notifiers.write().push(notifier); - self.sealing_work.lock().enabled = true; + /// Push listener that will handle new jobs + pub fn add_work_listener(&self, notifier: Box) { + self.listeners.write().push(notifier); + self.sealing.lock().enabled = true; } - /// Creates new instance of miner Arc. - pub fn new(options: MinerOptions, gas_pricer: GasPricer, spec: &Spec, accounts: Option>) -> Arc { - Arc::new(Miner::new_raw(options, gas_pricer, spec, accounts)) + /// Set a callback to be notified about imported transactions' hashes. + pub fn add_transactions_listener(&self, f: Box) { + self.transaction_queue.add_listener(f); } - /// Creates new instance of miner. - fn new_raw(options: MinerOptions, gas_pricer: GasPricer, spec: &Spec, accounts: Option>) -> Miner { - let gas_limit = match options.tx_queue_gas_limit { - GasLimit::Fixed(ref limit) => *limit, - _ => !U256::zero(), - }; - let mem_limit = options.tx_queue_memory_limit.unwrap_or_else(usize::max_value); - - let txq = TransactionQueue::with_limits( - options.tx_queue_strategy, - options.tx_queue_size, - mem_limit, - gas_limit, - options.tx_gas_limit - ); - let txq = match options.tx_queue_banning { - Banning::Disabled => BanningTransactionQueue::new(txq, Threshold::NeverBan, Duration::from_secs(180)), - Banning::Enabled { ban_duration, min_offends, .. } => BanningTransactionQueue::new( - txq, - Threshold::BanAfter(min_offends), - ban_duration, - ), - }; - - let notifiers: Vec> = match options.new_work_notify.is_empty() { - true => Vec::new(), - false => vec![Box::new(WorkPoster::new(&options.new_work_notify))], - }; - - let service_transaction_action = match options.refuse_service_transactions { - true => ServiceTransactionAction::Refuse, - false => ServiceTransactionAction::Check(ServiceTransactionChecker::default()), - }; + /// Creates new instance of miner Arc. + pub fn new(options: MinerOptions, gas_pricer: GasPricer, spec: &Spec, accounts: Option>) -> Self { + let limits = options.pool_limits.clone(); + let verifier_options = options.pool_verification_options.clone(); + let tx_queue_strategy = options.tx_queue_strategy; Miner { - transaction_queue: Arc::new(RwLock::new(txq)), - transaction_listener: RwLock::new(vec![]), - next_allowed_reseal: Mutex::new(Instant::now()), - next_mandatory_reseal: RwLock::new(Instant::now() + options.reseal_max_period), - sealing_block_last_request: Mutex::new(0), - sealing_work: Mutex::new(SealingWork{ + sealing: Mutex::new(SealingWork { queue: UsingQueue::new(options.work_queue_size), enabled: options.force_sealing - || !options.new_work_notify.is_empty() - || spec.engine.seals_internally().is_some() + || spec.engine.seals_internally().is_some(), + next_allowed_reseal: Instant::now(), + next_mandatory_reseal: Instant::now() + options.reseal_max_period, + last_request: None, }), - gas_range_target: RwLock::new((U256::zero(), U256::zero())), - author: RwLock::new(Address::default()), - extra_data: RwLock::new(Vec::new()), - options: options, - accounts: accounts, - engine: spec.engine.clone(), - notifiers: RwLock::new(notifiers), + params: RwLock::new(AuthoringParams::default()), + listeners: RwLock::new(vec![]), gas_pricer: Mutex::new(gas_pricer), - service_transaction_action: service_transaction_action, + nonce_cache: RwLock::new(HashMap::with_capacity(1024)), + options, + transaction_queue: Arc::new(TransactionQueue::new(limits, verifier_options, tx_queue_strategy)), + accounts, + engine: spec.engine.clone(), } } - /// Creates new instance of miner with accounts and with given spec. - pub fn with_spec_and_accounts(spec: &Spec, accounts: Option>) -> Miner { - Miner::new_raw(Default::default(), GasPricer::new_fixed(20_000_000_000u64.into()), spec, accounts) - } - - /// Creates new instance of miner without accounts, but with given spec. - pub fn with_spec(spec: &Spec) -> Miner { - Miner::new_raw(Default::default(), GasPricer::new_fixed(20_000_000_000u64.into()), spec, None) - } - - fn forced_sealing(&self) -> bool { - self.options.force_sealing || !self.notifiers.read().is_empty() + /// Creates new instance of miner with given spec and accounts. + /// + /// NOTE This should be only used for tests. + pub fn new_for_tests(spec: &Spec, accounts: Option>) -> Miner { + let minimal_gas_price = 0.into(); + Miner::new(MinerOptions { + pool_verification_options: pool::verifier::Options { + minimal_gas_price, + block_gas_limit: U256::max_value(), + tx_gas_limit: U256::max_value(), + }, + reseal_min_period: Duration::from_secs(0), + ..Default::default() + }, GasPricer::new_fixed(minimal_gas_price), spec, accounts) } /// Clear all pending block states pub fn clear(&self) { - self.sealing_work.lock().queue.reset(); - } - - /// Get `Some` `clone()` of the current pending block's state or `None` if we're not sealing. - pub fn pending_state(&self, latest_block_number: BlockNumber) -> Option> { - self.map_pending_block(|b| b.state().clone(), latest_block_number) + self.sealing.lock().queue.reset(); } - /// Get `Some` `clone()` of the current pending block or `None` if we're not sealing. - pub fn pending_block(&self, latest_block_number: BlockNumber) -> Option { - self.map_pending_block(|b| b.to_base(), latest_block_number) - } - - /// Get `Some` `clone()` of the current pending block header or `None` if we're not sealing. - pub fn pending_block_header(&self, latest_block_number: BlockNumber) -> Option
{ - self.map_pending_block(|b| b.header().clone(), latest_block_number) + /// Updates transaction queue verification limits. + /// + /// Limits consist of current block gas limit and minimal gas price. + pub fn update_transaction_queue_limits(&self, block_gas_limit: U256) { + trace!(target: "miner", "minimal_gas_price: recalibrating..."); + let txq = self.transaction_queue.clone(); + let mut options = self.options.pool_verification_options.clone(); + self.gas_pricer.lock().recalibrate(move |gas_price| { + debug!(target: "miner", "minimal_gas_price: Got gas price! {}", gas_price); + options.minimal_gas_price = gas_price; + options.block_gas_limit = block_gas_limit; + txq.set_verifier_options(options); + }); } - /// Set a callback to be notified about imported transactions' hashes. - pub fn add_transactions_listener(&self, f: Box) { - self.transaction_listener.write().push(f); + /// Retrieves an existing pending block iff it's not older than given block number. + /// + /// NOTE: This will not prepare a new pending block if it's not existing. + /// See `map_pending_block` for alternative behaviour. + fn map_existing_pending_block(&self, f: F, latest_block_number: BlockNumber) -> Option where + F: FnOnce(&ClosedBlock) -> T, + { + self.sealing.lock().queue + .peek_last_ref() + .and_then(|b| { + if b.block().header().number() > latest_block_number { + Some(f(b)) + } else { + None + } + }) } - fn map_pending_block(&self, f: F, latest_block_number: BlockNumber) -> Option where - F: FnOnce(&ClosedBlock) -> T, + fn pool_client<'a, C: 'a>(&'a self, chain: &'a C) -> PoolClient<'a, C> where + C: BlockChain + CallContract, { - self.from_pending_block( - latest_block_number, - || None, - |block| Some(f(block)), + PoolClient::new( + chain, + &self.nonce_cache, + &*self.engine, + self.accounts.as_ref().map(|x| &**x), + self.options.refuse_service_transactions, ) } /// Prepares new block for sealing including top transactions from queue. - fn prepare_block(&self, chain: &C) -> (ClosedBlock, Option) { + fn prepare_block(&self, chain: &C) -> (ClosedBlock, Option) where + C: BlockChain + CallContract + BlockProducer + Nonce + Sync, + { trace_time!("prepare_block"); let chain_info = chain.chain_info(); - let (transactions, mut open_block, original_work_hash) = { - let nonce_cap = if chain_info.best_block_number + 1 >= self.engine.params().dust_protection_transition { - Some((self.engine.params().nonce_cap_increment * (chain_info.best_block_number + 1)).into()) - } else { None }; - let transactions = {self.transaction_queue.read().top_transactions_at(chain_info.best_block_number, chain_info.best_block_timestamp, nonce_cap)}; - let mut sealing_work = self.sealing_work.lock(); - let last_work_hash = sealing_work.queue.peek_last_ref().map(|pb| pb.block().header().hash()); + + // Open block + let (mut open_block, original_work_hash) = { + let mut sealing = self.sealing.lock(); + let last_work_hash = sealing.queue.peek_last_ref().map(|pb| pb.block().header().hash()); let best_hash = chain_info.best_block_hash; // check to see if last ClosedBlock in would_seals is actually same parent block. @@ -407,7 +332,7 @@ impl Miner { // if at least one was pushed successfully, close and enqueue new ClosedBlock; // otherwise, leave everything alone. // otherwise, author a fresh block. - let mut open_block = match sealing_work.queue.pop_if(|b| b.block().header().parent_hash() == &best_hash) { + let mut open_block = match sealing.queue.pop_if(|b| b.block().header().parent_hash() == &best_hash) { Some(old_block) => { trace!(target: "miner", "prepare_block: Already have previous work; updating and returning"); // add transactions to old_block @@ -416,10 +341,11 @@ impl Miner { None => { // block not found - create it. trace!(target: "miner", "prepare_block: No existing work - making new block"); + let params = self.params.read().clone(); chain.prepare_open_block( - self.author(), - (self.gas_floor_target(), self.gas_ceil_target()), - self.extra_data() + params.author, + params.gas_range_target, + params.extra_data, ) } }; @@ -428,399 +354,401 @@ impl Miner { open_block.remove_gas_limit(); } - (transactions, open_block, last_work_hash) + (open_block, last_work_hash) }; let mut invalid_transactions = HashSet::new(); - let mut non_allowed_transactions = HashSet::new(); - let mut transactions_to_penalize = HashSet::new(); + let mut not_allowed_transactions = HashSet::new(); + let mut senders_to_penalize = HashSet::new(); let block_number = open_block.block().header().number(); - let mut tx_count: usize = 0; - let tx_total = transactions.len(); - for tx in transactions { - let hash = tx.hash(); + let mut tx_count = 0usize; + let mut skipped_transactions = 0usize; + + let client = self.pool_client(chain); + let engine_params = self.engine.params(); + let min_tx_gas: U256 = self.engine.schedule(chain_info.best_block_number).tx_gas.into(); + let nonce_cap: Option = if chain_info.best_block_number + 1 >= engine_params.dust_protection_transition { + Some((engine_params.nonce_cap_increment * (chain_info.best_block_number + 1)).into()) + } else { + None + }; + // we will never need more transactions than limit divided by min gas + let max_transactions = if min_tx_gas.is_zero() { + usize::max_value() + } else { + (*open_block.block().header().gas_limit() / min_tx_gas).as_u64() as usize + }; + + let pending: Vec> = self.transaction_queue.pending( + client.clone(), + pool::PendingSettings { + block_number: chain_info.best_block_number, + current_timestamp: chain_info.best_block_timestamp, + nonce_cap, + max_len: max_transactions, + ordering: miner::PendingOrdering::Priority, + } + ); + + let took_ms = |elapsed: &Duration| { + elapsed.as_secs() * 1000 + elapsed.subsec_nanos() as u64 / 1_000_000 + }; + + let block_start = Instant::now(); + debug!(target: "miner", "Attempting to push {} transactions.", pending.len()); + + for tx in pending { let start = Instant::now(); - // Check whether transaction type is allowed for sender - let result = match self.engine.machine().verify_transaction(&tx, open_block.header(), chain) { - Err(Error::Transaction(TransactionError::NotAllowed)) => { - Err(TransactionError::NotAllowed.into()) - } - _ => { - open_block.push_transaction(tx, None) - } - }; + + let transaction = tx.signed().clone(); + let hash = transaction.hash(); + let sender = transaction.sender(); + + // Re-verify transaction again vs current state. + let result = client.verify_signed(&transaction) + .map_err(|e| e.into()) + .and_then(|_| { + open_block.push_transaction(transaction, None) + }); + let took = start.elapsed(); // Check for heavy transactions - match self.options.tx_queue_banning { - Banning::Enabled { ref offend_threshold, .. } if &took > offend_threshold => { - match self.transaction_queue.write().ban_transaction(&hash) { - true => { - warn!(target: "miner", "Detected heavy transaction. Banning the sender and recipient/code."); - }, - false => { - transactions_to_penalize.insert(hash); - debug!(target: "miner", "Detected heavy transaction. Penalizing sender.") - } - } + match self.options.tx_queue_penalization { + Penalization::Enabled { ref offend_threshold } if &took > offend_threshold => { + senders_to_penalize.insert(sender); + debug!(target: "miner", "Detected heavy transaction ({} ms). Penalizing sender.", took_ms(&took)); }, _ => {}, } - trace!(target: "miner", "Adding tx {:?} took {:?}", hash, took); + + debug!(target: "miner", "Adding tx {:?} took {} ms", hash, took_ms(&took)); match result { - Err(Error::Execution(ExecutionError::BlockGasLimitReached { gas_limit, gas_used, gas })) => { + Err(Error(ErrorKind::Execution(ExecutionError::BlockGasLimitReached { gas_limit, gas_used, gas }), _)) => { debug!(target: "miner", "Skipping adding transaction to block because of gas limit: {:?} (limit: {:?}, used: {:?}, gas: {:?})", hash, gas_limit, gas_used, gas); // Penalize transaction if it's above current gas limit if gas > gas_limit { - transactions_to_penalize.insert(hash); + debug!(target: "txqueue", "[{:?}] Transaction above block gas limit.", hash); + invalid_transactions.insert(hash); } // Exit early if gas left is smaller then min_tx_gas - let min_tx_gas: U256 = 21000.into(); // TODO: figure this out properly. - if gas_limit - gas_used < min_tx_gas { + let gas_left = gas_limit - gas_used; + if gas_left < min_tx_gas { + debug!(target: "miner", "Remaining gas is lower than minimal gas for a transaction. Block is full."); + break; + } + + // Avoid iterating over the entire queue in case block is almost full. + skipped_transactions += 1; + if skipped_transactions > MAX_SKIPPED_TRANSACTIONS { + debug!(target: "miner", "Reached skipped transactions threshold. Assuming block is full."); break; } }, // Invalid nonce error can happen only if previous transaction is skipped because of gas limit. // If there is errornous state of transaction queue it will be fixed when next block is imported. - Err(Error::Execution(ExecutionError::InvalidNonce { expected, got })) => { + Err(Error(ErrorKind::Execution(ExecutionError::InvalidNonce { expected, got }), _)) => { debug!(target: "miner", "Skipping adding transaction to block because of invalid nonce: {:?} (expected: {:?}, got: {:?})", hash, expected, got); }, // already have transaction - ignore - Err(Error::Transaction(TransactionError::AlreadyImported)) => {}, - Err(Error::Transaction(TransactionError::NotAllowed)) => { - non_allowed_transactions.insert(hash); - debug!(target: "miner", - "Skipping non-allowed transaction for sender {:?}", - hash); + Err(Error(ErrorKind::Transaction(transaction::Error::AlreadyImported), _)) => {}, + Err(Error(ErrorKind::Transaction(transaction::Error::NotAllowed), _)) => { + not_allowed_transactions.insert(hash); + debug!(target: "miner", "Skipping non-allowed transaction for sender {:?}", hash); }, Err(e) => { + debug!(target: "txqueue", "[{:?}] Marking as invalid: {:?}.", hash, e); + debug!( + target: "miner", "Error adding transaction to block: number={}. transaction_hash={:?}, Error: {:?}", block_number, hash, e + ); invalid_transactions.insert(hash); - debug!(target: "miner", - "Error adding transaction to block: number={}. transaction_hash={:?}, Error: {:?}", - block_number, hash, e); }, - _ => { - tx_count += 1; - } // imported ok + // imported ok + _ => tx_count += 1, } } - trace!(target: "miner", "Pushed {}/{} transactions", tx_count, tx_total); + let elapsed = block_start.elapsed(); + debug!(target: "miner", "Pushed {} transactions in {} ms", tx_count, took_ms(&elapsed)); let block = open_block.close(); - let fetch_nonce = |a: &Address| chain.latest_nonce(a); - { - let mut queue = self.transaction_queue.write(); - for hash in invalid_transactions { - queue.remove(&hash, &fetch_nonce, RemovalReason::Invalid); - } - for hash in non_allowed_transactions { - queue.remove(&hash, &fetch_nonce, RemovalReason::NotAllowed); - } - for hash in transactions_to_penalize { - queue.penalize(&hash); - } + self.transaction_queue.remove(invalid_transactions.iter(), true); + self.transaction_queue.remove(not_allowed_transactions.iter(), false); + self.transaction_queue.penalize(senders_to_penalize.iter()); } + (block, original_work_hash) } - /// Asynchronously updates minimal gas price for transaction queue - pub fn recalibrate_minimal_gas_price(&self) { - debug!(target: "miner", "minimal_gas_price: recalibrating..."); - let txq = self.transaction_queue.clone(); - self.gas_pricer.lock().recalibrate(move |price| { - debug!(target: "miner", "minimal_gas_price: Got gas price! {}", price); - txq.write().set_minimal_gas_price(price); - }); + /// Returns `true` if we should create pending block even if some other conditions are not met. + /// + /// In general we always seal iff: + /// 1. --force-sealing CLI parameter is provided + /// 2. There are listeners awaiting new work packages (e.g. remote work notifications or stratum). + fn forced_sealing(&self) -> bool { + self.options.force_sealing || !self.listeners.read().is_empty() } /// Check is reseal is allowed and necessary. fn requires_reseal(&self, best_block: BlockNumber) -> bool { - let has_local_transactions = self.transaction_queue.read().has_local_pending_transactions(); - let mut sealing_work = self.sealing_work.lock(); - if sealing_work.enabled { - trace!(target: "miner", "requires_reseal: sealing enabled"); - let last_request = *self.sealing_block_last_request.lock(); - let should_disable_sealing = !self.forced_sealing() - && !has_local_transactions - && self.engine.seals_internally().is_none() - && best_block > last_request - && best_block - last_request > SEALING_TIMEOUT_IN_BLOCKS; - - trace!(target: "miner", "requires_reseal: should_disable_sealing={}; best_block={}, last_request={}", should_disable_sealing, best_block, last_request); - - if should_disable_sealing { - trace!(target: "miner", "Miner sleeping (current {}, last {})", best_block, last_request); - sealing_work.enabled = false; - sealing_work.queue.reset(); - false - } else { - // sealing enabled and we don't want to sleep. - *self.next_allowed_reseal.lock() = Instant::now() + self.options.reseal_min_period; - true - } - } else { + let mut sealing = self.sealing.lock(); + if !sealing.enabled { trace!(target: "miner", "requires_reseal: sealing is disabled"); + return false + } + + if !sealing.reseal_allowed() { + trace!(target: "miner", "requires_reseal: reseal too early"); + return false + } + + trace!(target: "miner", "requires_reseal: sealing enabled"); + + // Disable sealing if there were no requests for SEALING_TIMEOUT_IN_BLOCKS + let had_requests = sealing.last_request.map(|last_request| { + best_block > last_request + && best_block - last_request <= SEALING_TIMEOUT_IN_BLOCKS + }).unwrap_or(false); + + // keep sealing enabled if any of the conditions is met + let sealing_enabled = self.forced_sealing() + || self.transaction_queue.has_local_pending_transactions() + || self.engine.seals_internally() == Some(true) + || had_requests; + + let should_disable_sealing = !sealing_enabled; + + trace!(target: "miner", "requires_reseal: should_disable_sealing={}; forced={:?}, has_local={:?}, internal={:?}, had_requests={:?}", + should_disable_sealing, + self.forced_sealing(), + self.transaction_queue.has_local_pending_transactions(), + self.engine.seals_internally(), + had_requests, + ); + + if should_disable_sealing { + trace!(target: "miner", "Miner sleeping (current {}, last {})", best_block, sealing.last_request.unwrap_or(0)); + sealing.enabled = false; + sealing.queue.reset(); false + } else { + // sealing enabled and we don't want to sleep. + sealing.next_allowed_reseal = Instant::now() + self.options.reseal_min_period; + true } } /// Attempts to perform internal sealing (one that does not require work) and handles the result depending on the type of Seal. fn seal_and_import_block_internally(&self, chain: &C, block: ClosedBlock) -> bool - where C: BlockChain + SealedBlockImporter + where C: BlockChain + SealedBlockImporter, { - if !block.transactions().is_empty() || self.forced_sealing() || Instant::now() > *self.next_mandatory_reseal.read() { - trace!(target: "miner", "seal_block_internally: attempting internal seal."); + { + let sealing = self.sealing.lock(); + if block.transactions().is_empty() + && !self.forced_sealing() + && Instant::now() <= sealing.next_mandatory_reseal + { + return false + } + } - let parent_header = match chain.block_header(BlockId::Hash(*block.header().parent_hash())) { - Some(hdr) => hdr.decode(), - None => return false, - }; + trace!(target: "miner", "seal_block_internally: attempting internal seal."); - match self.engine.generate_seal(block.block(), &parent_header) { - // Save proposal for later seal submission and broadcast it. - Seal::Proposal(seal) => { - trace!(target: "miner", "Received a Proposal seal."); - *self.next_mandatory_reseal.write() = Instant::now() + self.options.reseal_max_period; - { - let mut sealing_work = self.sealing_work.lock(); - sealing_work.queue.push(block.clone()); - sealing_work.queue.use_last_ref(); - } - block - .lock() - .seal(&*self.engine, seal) - .map(|sealed| { chain.broadcast_proposal_block(sealed); true }) - .unwrap_or_else(|e| { - warn!("ERROR: seal failed when given internally generated seal: {}", e); - false - }) - }, - // Directly import a regular sealed block. - Seal::Regular(seal) => { - *self.next_mandatory_reseal.write() = Instant::now() + self.options.reseal_max_period; - block - .lock() - .seal(&*self.engine, seal) - .map(|sealed| chain.import_sealed_block(sealed).is_ok()) - .unwrap_or_else(|e| { - warn!("ERROR: seal failed when given internally generated seal: {}", e); - false - }) - }, - Seal::None => false, + let parent_header = match chain.block_header(BlockId::Hash(*block.header().parent_hash())) { + Some(h) => { + match h.decode() { + Ok(decoded_hdr) => decoded_hdr, + Err(_) => return false + } } - } else { - false + None => return false, + }; + + match self.engine.generate_seal(block.block(), &parent_header) { + // Save proposal for later seal submission and broadcast it. + Seal::Proposal(seal) => { + trace!(target: "miner", "Received a Proposal seal."); + { + let mut sealing = self.sealing.lock(); + sealing.next_mandatory_reseal = Instant::now() + self.options.reseal_max_period; + sealing.queue.push(block.clone()); + sealing.queue.use_last_ref(); + } + + block + .lock() + .seal(&*self.engine, seal) + .map(|sealed| { + chain.broadcast_proposal_block(sealed); + true + }) + .unwrap_or_else(|e| { + warn!("ERROR: seal failed when given internally generated seal: {}", e); + false + }) + }, + // Directly import a regular sealed block. + Seal::Regular(seal) => { + trace!(target: "miner", "Received a Regular seal."); + { + let mut sealing = self.sealing.lock(); + sealing.next_mandatory_reseal = Instant::now() + self.options.reseal_max_period; + } + + block + .lock() + .seal(&*self.engine, seal) + .map(|sealed| { + chain.import_sealed_block(sealed).is_ok() + }) + .unwrap_or_else(|e| { + warn!("ERROR: seal failed when given internally generated seal: {}", e); + false + }) + }, + Seal::None => false, } } /// Prepares work which has to be done to seal. fn prepare_work(&self, block: ClosedBlock, original_work_hash: Option) { let (work, is_new) = { - let mut sealing_work = self.sealing_work.lock(); - let last_work_hash = sealing_work.queue.peek_last_ref().map(|pb| pb.block().header().hash()); - trace!(target: "miner", "prepare_work: Checking whether we need to reseal: orig={:?} last={:?}, this={:?}", original_work_hash, last_work_hash, block.block().header().hash()); - let (work, is_new) = if last_work_hash.map_or(true, |h| h != block.block().header().hash()) { - trace!(target: "miner", "prepare_work: Pushing a new, refreshed or borrowed pending {}...", block.block().header().hash()); - let pow_hash = block.block().header().hash(); - let number = block.block().header().number(); - let difficulty = *block.block().header().difficulty(); - let is_new = original_work_hash.map_or(true, |h| block.block().header().hash() != h); - sealing_work.queue.push(block); + let block_header = block.block().header().clone(); + let block_hash = block_header.hash(); + + let mut sealing = self.sealing.lock(); + let last_work_hash = sealing.queue.peek_last_ref().map(|pb| pb.block().header().hash()); + + trace!( + target: "miner", + "prepare_work: Checking whether we need to reseal: orig={:?} last={:?}, this={:?}", + original_work_hash, last_work_hash, block_hash + ); + + let (work, is_new) = if last_work_hash.map_or(true, |h| h != block_hash) { + trace!( + target: "miner", + "prepare_work: Pushing a new, refreshed or borrowed pending {}...", + block_hash + ); + let is_new = original_work_hash.map_or(true, |h| h != block_hash); + + sealing.queue.push(block); // If push notifications are enabled we assume all work items are used. - if !self.notifiers.read().is_empty() && is_new { - sealing_work.queue.use_last_ref(); + if is_new && !self.listeners.read().is_empty() { + sealing.queue.use_last_ref(); } - (Some((pow_hash, difficulty, number)), is_new) + + (Some((block_hash, *block_header.difficulty(), block_header.number())), is_new) } else { (None, false) }; - trace!(target: "miner", "prepare_work: leaving (last={:?})", sealing_work.queue.peek_last_ref().map(|b| b.block().header().hash())); + trace!( + target: "miner", + "prepare_work: leaving (last={:?})", + sealing.queue.peek_last_ref().map(|b| b.block().header().hash()) + ); (work, is_new) }; if is_new { work.map(|(pow_hash, difficulty, number)| { - for notifier in self.notifiers.read().iter() { + for notifier in self.listeners.read().iter() { notifier.notify(pow_hash, difficulty, number) } }); } } - fn update_gas_limit(&self, client: &C) { - let gas_limit = client.best_block_header().gas_limit(); - let mut queue = self.transaction_queue.write(); - queue.set_gas_limit(gas_limit); - if let GasLimit::Auto = self.options.tx_queue_gas_limit { - // Set total tx queue gas limit to be 20x the block gas limit. - queue.set_total_gas_limit(gas_limit * 20u32); - } - } - /// Returns true if we had to prepare new pending block. - fn prepare_work_sealing(&self, client: &C) -> bool { - trace!(target: "miner", "prepare_work_sealing: entering"); + fn prepare_pending_block(&self, client: &C) -> bool where + C: BlockChain + CallContract + BlockProducer + SealedBlockImporter + Nonce + Sync, + { + trace!(target: "miner", "prepare_pending_block: entering"); let prepare_new = { - let mut sealing_work = self.sealing_work.lock(); - let have_work = sealing_work.queue.peek_last_ref().is_some(); - trace!(target: "miner", "prepare_work_sealing: have_work={}", have_work); + let mut sealing = self.sealing.lock(); + let have_work = sealing.queue.peek_last_ref().is_some(); + trace!(target: "miner", "prepare_pending_block: have_work={}", have_work); if !have_work { - sealing_work.enabled = true; + sealing.enabled = true; true } else { false } }; + if prepare_new { // -------------------------------------------------------------------------- - // | NOTE Code below requires transaction_queue and sealing_work locks. | + // | NOTE Code below requires sealing locks. | // | Make sure to release the locks before calling that method. | // -------------------------------------------------------------------------- let (block, original_work_hash) = self.prepare_block(client); self.prepare_work(block, original_work_hash); } - let mut sealing_block_last_request = self.sealing_block_last_request.lock(); + let best_number = client.chain_info().best_block_number; - if *sealing_block_last_request != best_number { - trace!(target: "miner", "prepare_work_sealing: Miner received request (was {}, now {}) - waking up.", *sealing_block_last_request, best_number); - *sealing_block_last_request = best_number; + let mut sealing = self.sealing.lock(); + if sealing.last_request != Some(best_number) { + trace!( + target: "miner", + "prepare_pending_block: Miner received request (was {}, now {}) - waking up.", + sealing.last_request.unwrap_or(0), best_number + ); + sealing.last_request = Some(best_number); } // Return if we restarted prepare_new } - fn add_transactions_to_queue( - &self, - client: &C, - transactions: Vec, - default_origin: TransactionOrigin, - condition: Option, - transaction_queue: &mut BanningTransactionQueue, - ) -> Vec> { - let best_block_header = client.best_block_header().decode(); - let insertion_time = client.chain_info().best_block_number; - let mut inserted = Vec::with_capacity(transactions.len()); - - let results = transactions.into_iter() - .map(|tx| { - let hash = tx.hash(); - if client.transaction_block(TransactionId::Hash(hash)).is_some() { - debug!(target: "miner", "Rejected tx {:?}: already in the blockchain", hash); - return Err(Error::Transaction(TransactionError::AlreadyImported)); - } - match self.engine.verify_transaction_basic(&tx, &best_block_header) - .and_then(|_| self.engine.verify_transaction_unordered(tx, &best_block_header)) - { - Err(e) => { - debug!(target: "miner", "Rejected tx {:?} with invalid signature: {:?}", hash, e); - Err(e) - }, - Ok(transaction) => { - // This check goes here because verify_transaction takes SignedTransaction parameter - self.engine.machine().verify_transaction(&transaction, &best_block_header, client)?; - - let origin = self.accounts.as_ref().and_then(|accounts| { - match accounts.has_account(transaction.sender()).unwrap_or(false) { - true => Some(TransactionOrigin::Local), - false => None, - } - }).unwrap_or(default_origin); - - let details_provider = TransactionDetailsProvider::new(client, &self.service_transaction_action); - let hash = transaction.hash(); - let result = match origin { - TransactionOrigin::Local | TransactionOrigin::RetractedBlock => { - transaction_queue.add(transaction, origin, insertion_time, condition.clone(), &details_provider)? - }, - TransactionOrigin::External => { - transaction_queue.add_with_banlist(transaction, insertion_time, &details_provider)? - }, - }; - - inserted.push(hash); - Ok(result) - }, - } - }) - .collect(); + /// Prepare pending block, check whether sealing is needed, and then update sealing. + fn prepare_and_update_sealing(&self, chain: &C) { + use miner::MinerService; - for listener in &*self.transaction_listener.read() { - listener(&inserted); + // Make sure to do it after transaction is imported and lock is dropped. + // We need to create pending block and enable sealing. + if self.engine.seals_internally().unwrap_or(false) || !self.prepare_pending_block(chain) { + // If new block has not been prepared (means we already had one) + // or Engine might be able to seal internally, + // we need to update sealing. + self.update_sealing(chain); } - - results - } - - /// Are we allowed to do a non-mandatory reseal? - fn tx_reseal_allowed(&self) -> bool { Instant::now() > *self.next_allowed_reseal.lock() } - - fn from_pending_block(&self, latest_block_number: BlockNumber, from_chain: F, map_block: G) -> H - where F: Fn() -> H, G: FnOnce(&ClosedBlock) -> H { - let sealing_work = self.sealing_work.lock(); - sealing_work.queue.peek_last_ref().map_or_else( - || from_chain(), - |b| { - if b.block().header().number() > latest_block_number { - map_block(b) - } else { - from_chain() - } - } - ) } } const SEALING_TIMEOUT_IN_BLOCKS : u64 = 5; -impl MinerService for Miner { +impl miner::MinerService for Miner { type State = State<::state_db::StateDB>; - fn clear_and_reset(&self, chain: &C) { - self.transaction_queue.write().clear(); - // -------------------------------------------------------------------------- - // | NOTE Code below requires transaction_queue and sealing_work locks. | - // | Make sure to release the locks before calling that method. | - // -------------------------------------------------------------------------- - self.update_sealing(chain); + fn authoring_params(&self) -> AuthoringParams { + self.params.read().clone() } - fn status(&self) -> MinerStatus { - let status = self.transaction_queue.read().status(); - let sealing_work = self.sealing_work.lock(); - MinerStatus { - transactions_in_pending_queue: status.pending, - transactions_in_future_queue: status.future, - transactions_in_pending_block: sealing_work.queue.peek_last_ref().map_or(0, |b| b.transactions().len()), - } + fn set_gas_range_target(&self, gas_range_target: (U256, U256)) { + self.params.write().gas_range_target = gas_range_target; } - fn set_author(&self, author: Address) { - if self.engine.seals_internally().is_some() { - let mut sealing_work = self.sealing_work.lock(); - sealing_work.enabled = true; - } - *self.author.write() = author; + fn set_extra_data(&self, extra_data: Bytes) { + self.params.write().extra_data = extra_data; } - fn set_engine_signer(&self, address: Address, password: String) -> Result<(), AccountError> { - if self.engine.seals_internally().is_some() { + fn set_author(&self, address: Address, password: Option) -> Result<(), AccountError> { + self.params.write().author = address; + + if self.engine.seals_internally().is_some() && password.is_some() { if let Some(ref ap) = self.accounts { + let password = password.unwrap_or_default(); + // Sign test message ap.sign(address.clone(), Some(password.clone()), Default::default())?; - // Limit the scope of the locks. - { - let mut sealing_work = self.sealing_work.lock(); - sealing_work.enabled = true; - *self.author.write() = address; - } + // Enable sealing + self.sealing.lock().enabled = true; // -------------------------------------------------------------------------- - // | NOTE Code below may require author and sealing_work locks | - // | (some `Engine`s call `EngineClient.update_sealing()`) |. + // | NOTE Code below may require author and sealing locks | + // | (some `Engine`s call `EngineClient.update_sealing()`) | // | Make sure to release the locks before calling that method. | // -------------------------------------------------------------------------- self.engine.set_signer(ap.clone(), address, password); @@ -830,351 +758,278 @@ impl MinerService for Miner { Err(AccountError::NotFound) } } else { - warn!(target: "miner", "Cannot set engine signer on a PoW chain."); - Err(AccountError::InappropriateChain) + Ok(()) } } - fn set_extra_data(&self, extra_data: Bytes) { - *self.extra_data.write() = extra_data; - } - - /// Set the gas limit we wish to target when sealing a new block. - fn set_gas_floor_target(&self, target: U256) { - self.gas_range_target.write().0 = target; - } - - fn set_gas_ceil_target(&self, target: U256) { - self.gas_range_target.write().1 = target; - } - - fn set_minimal_gas_price(&self, min_gas_price: U256) { - self.transaction_queue.write().set_minimal_gas_price(min_gas_price); - } - - fn minimal_gas_price(&self) -> U256 { - *self.transaction_queue.read().minimal_gas_price() - } - fn sensible_gas_price(&self) -> U256 { // 10% above our minimum. - *self.transaction_queue.read().minimal_gas_price() * 110u32 / 100.into() + self.transaction_queue.current_worst_gas_price() * 110u32 / 100.into() } fn sensible_gas_limit(&self) -> U256 { - self.gas_range_target.read().0 / 5.into() - } - - fn transactions_limit(&self) -> usize { - self.transaction_queue.read().limit() - } - - fn set_transactions_limit(&self, limit: usize) { - self.transaction_queue.write().set_limit(limit) - } - - fn set_tx_gas_limit(&self, limit: U256) { - self.transaction_queue.write().set_tx_gas_limit(limit) - } - - /// Get the author that we will seal blocks as. - fn author(&self) -> Address { - *self.author.read() - } - - /// Get the extra_data that we will seal blocks with. - fn extra_data(&self) -> Bytes { - self.extra_data.read().clone() + self.params.read().gas_range_target.0 / 5.into() } - /// Get the gas limit we wish to target when sealing a new block. - fn gas_floor_target(&self) -> U256 { - self.gas_range_target.read().0 - } - - /// Get the gas limit we wish to target when sealing a new block. - fn gas_ceil_target(&self) -> U256 { - self.gas_range_target.read().1 - } - - fn import_external_transactions( + fn import_external_transactions( &self, - client: &C, + chain: &C, transactions: Vec - ) -> Vec> { + ) -> Vec> { trace!(target: "external_tx", "Importing external transactions"); - let results = { - let mut transaction_queue = self.transaction_queue.write(); - self.add_transactions_to_queue( - client, transactions, TransactionOrigin::External, None, &mut transaction_queue - ) - }; + let client = self.pool_client(chain); + let results = self.transaction_queue.import( + client, + transactions.into_iter().map(pool::verifier::Transaction::Unverified).collect(), + ); - if !results.is_empty() && self.options.reseal_on_external_tx && self.tx_reseal_allowed() { - // -------------------------------------------------------------------------- - // | NOTE Code below requires transaction_queue and sealing_work locks. | - // | Make sure to release the locks before calling that method. | - // -------------------------------------------------------------------------- - self.update_sealing(client); + // -------------------------------------------------------------------------- + // | NOTE Code below requires sealing locks. | + // | Make sure to release the locks before calling that method. | + // -------------------------------------------------------------------------- + if !results.is_empty() && self.options.reseal_on_external_tx && self.sealing.lock().reseal_allowed() { + self.prepare_and_update_sealing(chain); } + results } - fn import_own_transaction( + fn import_own_transaction( &self, chain: &C, - pending: PendingTransaction, - ) -> Result { + pending: PendingTransaction + ) -> Result<(), transaction::Error> { + // note: you may want to use `import_claimed_local_transaction` instead of this one. trace!(target: "own_tx", "Importing transaction: {:?}", pending); - let imported = { - // Be sure to release the lock before we call prepare_work_sealing - let mut transaction_queue = self.transaction_queue.write(); - // We need to re-validate transactions - let import = self.add_transactions_to_queue( - chain, vec![pending.transaction.into()], TransactionOrigin::Local, pending.condition, &mut transaction_queue - ).pop().expect("one result returned per added transaction; one added => one result; qed"); - - match import { - Ok(_) => { - trace!(target: "own_tx", "Status: {:?}", transaction_queue.status()); - }, - Err(ref e) => { - trace!(target: "own_tx", "Status: {:?}", transaction_queue.status()); - warn!(target: "own_tx", "Error importing transaction: {:?}", e); - }, - } - import - }; + let client = self.pool_client(chain); + let imported = self.transaction_queue.import( + client, + vec![pool::verifier::Transaction::Local(pending)] + ).pop().expect("one result returned per added transaction; one added => one result; qed"); // -------------------------------------------------------------------------- - // | NOTE Code below requires transaction_queue and sealing_work locks. | + // | NOTE Code below requires sealing locks. | // | Make sure to release the locks before calling that method. | // -------------------------------------------------------------------------- - if imported.is_ok() && self.options.reseal_on_own_tx && self.tx_reseal_allowed() { - // Make sure to do it after transaction is imported and lock is droped. - // We need to create pending block and enable sealing. - if self.engine.seals_internally().unwrap_or(false) || !self.prepare_work_sealing(chain) { - // If new block has not been prepared (means we already had one) - // or Engine might be able to seal internally, - // we need to update sealing. - self.update_sealing(chain); - } + if imported.is_ok() && self.options.reseal_on_own_tx && self.sealing.lock().reseal_allowed() { + self.prepare_and_update_sealing(chain); } imported } - fn pending_transactions(&self) -> Vec { - let queue = self.transaction_queue.read(); - queue.pending_transactions(BlockNumber::max_value(), u64::max_value()) + fn import_claimed_local_transaction( + &self, + chain: &C, + pending: PendingTransaction, + trusted: bool + ) -> Result<(), transaction::Error> { + // treat the tx as local if the option is enabled, or if we have the account + let sender = pending.sender(); + let treat_as_local = trusted + || !self.options.tx_queue_no_unfamiliar_locals + || self.accounts.as_ref().map(|accts| accts.has_account(sender)).unwrap_or(false); + + if treat_as_local { + self.import_own_transaction(chain, pending) + } else { + // We want to replicate behaviour for external transactions if we're not going to treat + // this as local. This is important with regards to sealing blocks + self.import_external_transactions(chain, vec![pending.transaction.into()]) + .pop().expect("one result per tx, as in `import_own_transaction`") + } } - fn local_transactions(&self) -> BTreeMap { - let queue = self.transaction_queue.read(); - queue.local_transactions() - .iter() - .map(|(hash, status)| (*hash, status.clone())) - .collect() + fn local_transactions(&self) -> BTreeMap { + self.transaction_queue.local_transactions() } - fn future_transactions(&self) -> Vec { - self.transaction_queue.read().future_transactions() + fn queued_transactions(&self) -> Vec> { + self.transaction_queue.all_transactions() } - fn ready_transactions(&self, best_block: BlockNumber, best_block_timestamp: u64) -> Vec { - let queue = self.transaction_queue.read(); - match self.options.pending_set { - PendingSet::AlwaysQueue => queue.pending_transactions(best_block, best_block_timestamp), - PendingSet::SealingOrElseQueue => { - self.from_pending_block( - best_block, - || queue.pending_transactions(best_block, best_block_timestamp), - |sealing| sealing.transactions().iter().map(|t| t.clone().into()).collect() - ) - }, - PendingSet::AlwaysSealing => { - self.from_pending_block( - best_block, - || vec![], - |sealing| sealing.transactions().iter().map(|t| t.clone().into()).collect() - ) - }, - } - } + fn ready_transactions(&self, chain: &C, max_len: usize, ordering: miner::PendingOrdering) + -> Vec> + where + C: ChainInfo + Nonce + Sync, + { + let chain_info = chain.chain_info(); + + let from_queue = || { + // We propagate transactions over the nonce cap. + // The mechanism is only to limit number of transactions in pending block + // those transactions are valid and will just be ready to be included in next block. + let nonce_cap = None; + + self.transaction_queue.pending( + CachedNonceClient::new(chain, &self.nonce_cache), + pool::PendingSettings { + block_number: chain_info.best_block_number, + current_timestamp: chain_info.best_block_timestamp, + nonce_cap, + max_len, + ordering, + }, + ) + }; + + let from_pending = || { + self.map_existing_pending_block(|sealing| { + sealing.transactions() + .iter() + .map(|signed| pool::VerifiedTransaction::from_pending_block_transaction(signed.clone())) + .map(Arc::new) + .take(max_len) + .collect() + }, chain_info.best_block_number) + }; - fn pending_transactions_hashes(&self, best_block: BlockNumber) -> Vec { - let queue = self.transaction_queue.read(); match self.options.pending_set { - PendingSet::AlwaysQueue => queue.pending_hashes(), - PendingSet::SealingOrElseQueue => { - self.from_pending_block( - best_block, - || queue.pending_hashes(), - |sealing| sealing.transactions().iter().map(|t| t.hash()).collect() - ) + PendingSet::AlwaysQueue => { + from_queue() }, PendingSet::AlwaysSealing => { - self.from_pending_block( - best_block, - || vec![], - |sealing| sealing.transactions().iter().map(|t| t.hash()).collect() - ) + from_pending().unwrap_or_default() }, - } - } - - fn transaction(&self, best_block: BlockNumber, hash: &H256) -> Option { - let queue = self.transaction_queue.read(); - match self.options.pending_set { - PendingSet::AlwaysQueue => queue.find(hash), PendingSet::SealingOrElseQueue => { - self.from_pending_block( - best_block, - || queue.find(hash), - |sealing| sealing.transactions().iter().find(|t| &t.hash() == hash).cloned().map(Into::into) - ) - }, - PendingSet::AlwaysSealing => { - self.from_pending_block( - best_block, - || None, - |sealing| sealing.transactions().iter().find(|t| &t.hash() == hash).cloned().map(Into::into) - ) + from_pending().unwrap_or_else(from_queue) }, } } - fn remove_pending_transaction(&self, chain: &C, hash: &H256) -> Option { - let mut queue = self.transaction_queue.write(); - let tx = queue.find(hash); - if tx.is_some() { - let fetch_nonce = |a: &Address| chain.latest_nonce(a); - queue.remove(hash, &fetch_nonce, RemovalReason::Canceled); - } - tx + fn next_nonce(&self, chain: &C, address: &Address) -> U256 where + C: Nonce + Sync, + { + self.transaction_queue.next_nonce(CachedNonceClient::new(chain, &self.nonce_cache), address) + .unwrap_or_else(|| chain.latest_nonce(address)) } - fn pending_receipt(&self, best_block: BlockNumber, hash: &H256) -> Option { - self.from_pending_block( - best_block, - || None, - |pending| { - let txs = pending.transactions(); - txs.iter() - .map(|t| t.hash()) - .position(|t| t == *hash) - .map(|index| { - let prev_gas = if index == 0 { Default::default() } else { pending.receipts()[index - 1].gas_used }; - let tx = &txs[index]; - let receipt = &pending.receipts()[index]; - RichReceipt { - transaction_hash: hash.clone(), - transaction_index: index, - cumulative_gas_used: receipt.gas_used, - gas_used: receipt.gas_used - prev_gas, - contract_address: match tx.action { - Action::Call(_) => None, - Action::Create => { - let sender = tx.sender(); - Some(contract_address(self.engine.create_address_scheme(pending.header().number()), &sender, &tx.nonce, &tx.data).0) - } - }, - logs: receipt.logs.clone(), - log_bloom: receipt.log_bloom, - outcome: receipt.outcome.clone(), - } - }) - } - ) + fn transaction(&self, hash: &H256) -> Option> { + self.transaction_queue.find(hash) } - fn pending_receipts(&self, best_block: BlockNumber) -> BTreeMap { - self.from_pending_block( - best_block, - BTreeMap::new, - |pending| { - let hashes = pending.transactions() - .iter() - .map(|t| t.hash()); - - let receipts = pending.receipts().iter().cloned(); + fn remove_transaction(&self, hash: &H256) -> Option> { + self.transaction_queue.remove(::std::iter::once(hash), false) + .pop() + .expect("remove() returns one result per hash; one hash passed; qed") + } - hashes.zip(receipts).collect() - } - ) + fn queue_status(&self) -> QueueStatus { + self.transaction_queue.status() } - fn last_nonce(&self, address: &Address) -> Option { - self.transaction_queue.read().last_nonce(address) + fn pending_receipt(&self, best_block: BlockNumber, hash: &H256) -> Option { + self.map_existing_pending_block(|pending| { + let txs = pending.transactions(); + txs.iter() + .map(|t| t.hash()) + .position(|t| t == *hash) + .map(|index| { + let receipts = pending.receipts(); + let prev_gas = if index == 0 { Default::default() } else { receipts[index - 1].gas_used }; + let tx = &txs[index]; + let receipt = &receipts[index]; + RichReceipt { + transaction_hash: hash.clone(), + transaction_index: index, + cumulative_gas_used: receipt.gas_used, + gas_used: receipt.gas_used - prev_gas, + contract_address: match tx.action { + Action::Call(_) => None, + Action::Create => { + let sender = tx.sender(); + Some(contract_address(self.engine.create_address_scheme(pending.header().number()), &sender, &tx.nonce, &tx.data).0) + } + }, + logs: receipt.logs.clone(), + log_bloom: receipt.log_bloom, + outcome: receipt.outcome.clone(), + } + }) + }, best_block).and_then(|x| x) } - fn can_produce_work_package(&self) -> bool { - self.engine.seals_internally().is_none() + fn pending_receipts(&self, best_block: BlockNumber) -> Option> { + self.map_existing_pending_block(|pending| { + let hashes = pending.transactions().iter().map(|t| t.hash()); + let receipts = pending.receipts().iter().cloned(); + + hashes.zip(receipts).collect() + }, best_block) } /// Update sealing if required. /// Prepare the block and work if the Engine does not seal internally. - fn update_sealing(&self, chain: &C) - where C: AccountData + BlockChain + RegistryInfo - + CallContract + BlockProducer + SealedBlockImporter + fn update_sealing(&self, chain: &C) where + C: BlockChain + CallContract + BlockProducer + SealedBlockImporter + Nonce + Sync, { trace!(target: "miner", "update_sealing"); - const NO_NEW_CHAIN_WITH_FORKS: &str = "Your chain specification contains one or more hard forks which are required to be \ - on by default. Please remove these forks and start your chain again."; - if self.requires_reseal(chain.chain_info().best_block_number) { - // -------------------------------------------------------------------------- - // | NOTE Code below requires transaction_queue and sealing_work locks. | - // | Make sure to release the locks before calling that method. | - // -------------------------------------------------------------------------- - trace!(target: "miner", "update_sealing: preparing a block"); - let (block, original_work_hash) = self.prepare_block(chain); - - // refuse to seal the first block of the chain if it contains hard forks - // which should be on by default. - if block.block().header().number() == 1 && self.engine.params().contains_bugfix_hard_fork() { - warn!("{}", NO_NEW_CHAIN_WITH_FORKS); - return; - } + // Do nothing if reseal is not required, + // but note that `requires_reseal` updates internal state. + if !self.requires_reseal(chain.chain_info().best_block_number) { + return; + } - match self.engine.seals_internally() { - Some(true) => { - trace!(target: "miner", "update_sealing: engine indicates internal sealing"); - if self.seal_and_import_block_internally(chain, block) { - trace!(target: "miner", "update_sealing: imported internally sealed block"); - } - }, - Some(false) => trace!(target: "miner", "update_sealing: engine is not keen to seal internally right now"), - None => { - trace!(target: "miner", "update_sealing: engine does not seal internally, preparing work"); - self.prepare_work(block, original_work_hash) - }, - } + // -------------------------------------------------------------------------- + // | NOTE Code below requires sealing locks. | + // | Make sure to release the locks before calling that method. | + // -------------------------------------------------------------------------- + trace!(target: "miner", "update_sealing: preparing a block"); + let (block, original_work_hash) = self.prepare_block(chain); + + // refuse to seal the first block of the chain if it contains hard forks + // which should be on by default. + if block.block().header().number() == 1 && self.engine.params().contains_bugfix_hard_fork() { + warn!("Your chain specification contains one or more hard forks which are required to be \ + on by default. Please remove these forks and start your chain again."); + return; + } + + match self.engine.seals_internally() { + Some(true) => { + trace!(target: "miner", "update_sealing: engine indicates internal sealing"); + if self.seal_and_import_block_internally(chain, block) { + trace!(target: "miner", "update_sealing: imported internally sealed block"); + } + }, + Some(false) => { + trace!(target: "miner", "update_sealing: engine is not keen to seal internally right now"); + // anyway, save the block for later use + self.sealing.lock().queue.push(block); + }, + None => { + trace!(target: "miner", "update_sealing: engine does not seal internally, preparing work"); + self.prepare_work(block, original_work_hash) + }, } } fn is_currently_sealing(&self) -> bool { - self.sealing_work.lock().queue.is_in_use() + self.sealing.lock().enabled } - fn map_sealing_work(&self, client: &C, f: F) -> Option - where C: AccountData + BlockChain + BlockProducer + CallContract, - F: FnOnce(&ClosedBlock) -> T + fn work_package(&self, chain: &C) -> Option<(H256, BlockNumber, u64, U256)> where + C: BlockChain + CallContract + BlockProducer + SealedBlockImporter + Nonce + Sync, { - trace!(target: "miner", "map_sealing_work: entering"); - self.prepare_work_sealing(client); - trace!(target: "miner", "map_sealing_work: sealing prepared"); - let mut sealing_work = self.sealing_work.lock(); - let ret = sealing_work.queue.use_last_ref(); - trace!(target: "miner", "map_sealing_work: leaving use_last_ref={:?}", ret.as_ref().map(|b| b.block().header().hash())); - ret.map(f) + if self.engine.seals_internally().is_some() { + return None; + } + + self.prepare_pending_block(chain); + + self.sealing.lock().queue.use_last_ref().map(|b| { + let header = b.header(); + (header.hash(), header.number(), header.timestamp(), *header.difficulty()) + }) } - fn submit_seal(&self, chain: &C, block_hash: H256, seal: Vec) -> Result<(), Error> { + // Note used for external submission (PoW) and internally by sealing engines. + fn submit_seal(&self, block_hash: H256, seal: Vec) -> Result { let result = - if let Some(b) = self.sealing_work.lock().queue.get_used_if( + if let Some(b) = self.sealing.lock().queue.get_used_if( if self.options.enable_resubmission { GetAction::Clone } else { @@ -1185,24 +1040,23 @@ impl MinerService for Miner { trace!(target: "miner", "Submitted block {}={}={} with seal {:?}", block_hash, b.hash(), b.header().bare_hash(), seal); b.lock().try_seal(&*self.engine, seal).or_else(|(e, _)| { warn!(target: "miner", "Mined solution rejected: {}", e); - Err(Error::PowInvalid) + Err(ErrorKind::PowInvalid.into()) }) } else { warn!(target: "miner", "Submitted solution rejected: Block unknown or out of date."); - Err(Error::PowHashInvalid) + Err(ErrorKind::PowHashInvalid.into()) }; + result.and_then(|sealed| { let n = sealed.header().number(); let h = sealed.header().hash(); - chain.import_sealed_block(sealed)?; info!(target: "miner", "Submitted block imported OK. #{}: {}", Colour::White.bold().paint(format!("{}", n)), Colour::White.bold().paint(format!("{:x}", h))); - Ok(()) + Ok(sealed) }) } - fn chain_new_blocks(&self, chain: &C, imported: &[H256], _invalid: &[H256], enacted: &[H256], retracted: &[H256]) - where C: AccountData + BlockChain + CallContract + RegistryInfo - + BlockProducer + ScheduleInfo + SealedBlockImporter + fn chain_new_blocks(&self, chain: &C, imported: &[H256], _invalid: &[H256], enacted: &[H256], retracted: &[H256], is_internal_import: bool) + where C: miner::BlockChainClient, { trace!(target: "miner", "chain_new_blocks"); @@ -1210,134 +1064,89 @@ impl MinerService for Miner { // 2. We ignore blocks that are `invalid` because it doesn't have any meaning in terms of the transactions that // are in those blocks - // First update gas limit in transaction queue - self.update_gas_limit(chain); + // Clear nonce cache + self.nonce_cache.write().clear(); - // Update minimal gas price - self.recalibrate_minimal_gas_price(); + // First update gas limit in transaction queue and minimal gas price. + let gas_limit = *chain.best_block_header().gas_limit(); + self.update_transaction_queue_limits(gas_limit); // Then import all transactions... + let client = self.pool_client(chain); { - - let mut transaction_queue = self.transaction_queue.write(); - for hash in retracted { - let block = chain.block(BlockId::Hash(*hash)) - .expect("Client is sending message after commit to db and inserting to chain; the block is available; qed"); - let txs = block.transactions(); - let _ = self.add_transactions_to_queue( - chain, txs, TransactionOrigin::RetractedBlock, None, &mut transaction_queue - ); - } + retracted + .par_iter() + .for_each(|hash| { + let block = chain.block(BlockId::Hash(*hash)) + .expect("Client is sending message after commit to db and inserting to chain; the block is available; qed"); + let txs = block.transactions() + .into_iter() + .map(pool::verifier::Transaction::Retracted) + .collect(); + let _ = self.transaction_queue.import( + client.clone(), + txs, + ); + }); } // ...and at the end remove the old ones - { - let fetch_account = |a: &Address| AccountDetails { - nonce: chain.latest_nonce(a), - balance: chain.latest_balance(a), - }; - let time = chain.chain_info().best_block_number; - let mut transaction_queue = self.transaction_queue.write(); - transaction_queue.remove_old(&fetch_account, time); - } + self.transaction_queue.cull(client); if enacted.len() > 0 || (imported.len() > 0 && self.options.reseal_on_uncle) { - // -------------------------------------------------------------------------- - // | NOTE Code below requires transaction_queue and sealing_work locks. | - // | Make sure to release the locks before calling that method. | - // -------------------------------------------------------------------------- - self.update_sealing(chain); + // Reset `next_allowed_reseal` in case a block is imported. + // Even if min_period is high, we will always attempt to create + // new pending block. + self.sealing.lock().next_allowed_reseal = Instant::now(); + + if !is_internal_import { + // -------------------------------------------------------------------------- + // | NOTE Code below requires sealing locks. | + // | Make sure to release the locks before calling that method. | + // -------------------------------------------------------------------------- + self.update_sealing(chain); + } } } fn pending_state(&self, latest_block_number: BlockNumber) -> Option { - Miner::pending_state(self, latest_block_number) + self.map_existing_pending_block(|b| b.state().clone(), latest_block_number) } fn pending_block_header(&self, latest_block_number: BlockNumber) -> Option
{ - Miner::pending_block_header(self, latest_block_number) + self.map_existing_pending_block(|b| b.header().clone(), latest_block_number) } fn pending_block(&self, latest_block_number: BlockNumber) -> Option { - Miner::pending_block(self, latest_block_number) - } -} - -/// Action when service transaction is received -enum ServiceTransactionAction { - /// Refuse service transaction immediately - Refuse, - /// Accept if sender is certified to send service transactions - Check(ServiceTransactionChecker), -} - -impl ServiceTransactionAction { - pub fn check(&self, client: &C, tx: &SignedTransaction) -> Result - { - match *self { - ServiceTransactionAction::Refuse => Err("configured to refuse service transactions".to_owned()), - ServiceTransactionAction::Check(ref checker) => checker.check(client, tx), - } - } -} - -struct TransactionDetailsProvider<'a, C: 'a> { - client: &'a C, - service_transaction_action: &'a ServiceTransactionAction, -} - -impl<'a, C> TransactionDetailsProvider<'a, C> { - pub fn new(client: &'a C, service_transaction_action: &'a ServiceTransactionAction) -> Self { - TransactionDetailsProvider { - client: client, - service_transaction_action: service_transaction_action, - } - } -} - -impl<'a, C> TransactionQueueDetailsProvider for TransactionDetailsProvider<'a, C> - where C: AccountData + CallContract + RegistryInfo + ScheduleInfo -{ - fn fetch_account(&self, address: &Address) -> AccountDetails { - AccountDetails { - nonce: self.client.latest_nonce(address), - balance: self.client.latest_balance(address), - } + self.map_existing_pending_block(|b| b.to_base(), latest_block_number) } - fn estimate_gas_required(&self, tx: &SignedTransaction) -> U256 { - tx.gas_required(&self.client.latest_schedule()).into() - } - - fn is_service_transaction_acceptable(&self, tx: &SignedTransaction) -> Result { - self.service_transaction_action.check(self.client, tx) + fn pending_transactions(&self, latest_block_number: BlockNumber) -> Option> { + self.map_existing_pending_block(|b| b.transactions().into_iter().cloned().collect(), latest_block_number) } } #[cfg(test)] mod tests { use super::*; - use ethcore_miner::transaction_queue::PrioritizationStrategy; - use ethereum_types::U256; use ethkey::{Generator, Random}; - use client::{TestBlockChainClient, EachBlockWith, ChainInfo}; use hash::keccak; use header::BlockNumber; use rustc_hex::FromHex; - use spec::Spec; - use transaction::{SignedTransaction, Transaction, PendingTransaction, Action}; - use miner::MinerService; - use tests::helpers::{generate_dummy_client, generate_dummy_client_with_spec_and_accounts}; + use client::{TestBlockChainClient, EachBlockWith, ChainInfo, ImportSealedBlock}; + use miner::{MinerService, PendingOrdering}; + use test_helpers::{generate_dummy_client, generate_dummy_client_with_spec_and_accounts}; + use transaction::{Transaction}; #[test] fn should_prepare_block_to_seal() { // given let client = TestBlockChainClient::default(); - let miner = Miner::with_spec(&Spec::new_test()); + let miner = Miner::new_for_tests(&Spec::new_test(), None); // when - let sealing_work = miner.map_sealing_work(&client, |_| ()); + let sealing_work = miner.work_package(&client); assert!(sealing_work.is_some(), "Expected closed block"); } @@ -1345,53 +1154,58 @@ mod tests { fn should_still_work_after_a_couple_of_blocks() { // given let client = TestBlockChainClient::default(); - let miner = Miner::with_spec(&Spec::new_test()); + let miner = Miner::new_for_tests(&Spec::new_test(), None); - let res = miner.map_sealing_work(&client, |b| b.block().header().hash()); - assert!(res.is_some()); - assert!(miner.submit_seal(&client, res.unwrap(), vec![]).is_ok()); + let res = miner.work_package(&client); + let hash = res.unwrap().0; + let block = miner.submit_seal(hash, vec![]).unwrap(); + client.import_sealed_block(block).unwrap(); // two more blocks mined, work requested. client.add_blocks(1, EachBlockWith::Uncle); - miner.map_sealing_work(&client, |b| b.block().header().hash()); + miner.work_package(&client); client.add_blocks(1, EachBlockWith::Uncle); - miner.map_sealing_work(&client, |b| b.block().header().hash()); + miner.work_package(&client); // solution to original work submitted. - assert!(miner.submit_seal(&client, res.unwrap(), vec![]).is_ok()); + assert!(miner.submit_seal(hash, vec![]).is_ok()); } fn miner() -> Miner { - Arc::try_unwrap(Miner::new( + Miner::new( MinerOptions { - new_work_notify: Vec::new(), force_sealing: false, reseal_on_external_tx: false, reseal_on_own_tx: true, reseal_on_uncle: false, reseal_min_period: Duration::from_secs(5), reseal_max_period: Duration::from_secs(120), - tx_gas_limit: !U256::zero(), - tx_queue_size: 1024, - tx_queue_memory_limit: None, - tx_queue_gas_limit: GasLimit::None, - tx_queue_strategy: PrioritizationStrategy::GasFactorAndGasPrice, pending_set: PendingSet::AlwaysSealing, work_queue_size: 5, enable_resubmission: true, - tx_queue_banning: Banning::Disabled, - refuse_service_transactions: false, infinite_pending_block: false, + tx_queue_penalization: Penalization::Disabled, + tx_queue_strategy: PrioritizationStrategy::GasPriceOnly, + tx_queue_no_unfamiliar_locals: false, + refuse_service_transactions: false, + pool_limits: Default::default(), + pool_verification_options: pool::verifier::Options { + minimal_gas_price: 0.into(), + block_gas_limit: U256::max_value(), + tx_gas_limit: U256::max_value(), + }, }, GasPricer::new_fixed(0u64.into()), &Spec::new_test(), None, // accounts provider - )).ok().expect("Miner was just created.") + ) } + const TEST_CHAIN_ID: u64 = 2; + fn transaction() -> SignedTransaction { - transaction_with_chain_id(2) + transaction_with_chain_id(TEST_CHAIN_ID) } fn transaction_with_chain_id(chain_id: u64) -> SignedTransaction { @@ -1417,13 +1231,12 @@ mod tests { let res = miner.import_own_transaction(&client, PendingTransaction::new(transaction, None)); // then - assert_eq!(res.unwrap(), TransactionImportResult::Current); - assert_eq!(miner.pending_transactions().len(), 1); - assert_eq!(miner.ready_transactions(best_block, 0).len(), 1); - assert_eq!(miner.pending_transactions_hashes(best_block).len(), 1); - assert_eq!(miner.pending_receipts(best_block).len(), 1); + assert_eq!(res.unwrap(), ()); + assert_eq!(miner.pending_transactions(best_block).unwrap().len(), 1); + assert_eq!(miner.pending_receipts(best_block).unwrap().len(), 1); + assert_eq!(miner.ready_transactions(&client, 10, PendingOrdering::Priority).len(), 1); // This method will let us know if pending block was created (before calling that method) - assert!(!miner.prepare_work_sealing(&client)); + assert!(!miner.prepare_pending_block(&client)); } #[test] @@ -1437,11 +1250,10 @@ mod tests { let res = miner.import_own_transaction(&client, PendingTransaction::new(transaction, None)); // then - assert_eq!(res.unwrap(), TransactionImportResult::Current); - assert_eq!(miner.pending_transactions().len(), 1); - assert_eq!(miner.ready_transactions(best_block, 0).len(), 0); - assert_eq!(miner.pending_transactions_hashes(best_block).len(), 0); - assert_eq!(miner.pending_receipts(best_block).len(), 0); + assert_eq!(res.unwrap(), ()); + assert_eq!(miner.pending_transactions(best_block), None); + assert_eq!(miner.pending_receipts(best_block), None); + assert_eq!(miner.ready_transactions(&client, 10, PendingOrdering::Priority).len(), 1); } #[test] @@ -1455,13 +1267,63 @@ mod tests { let res = miner.import_external_transactions(&client, vec![transaction]).pop().unwrap(); // then - assert_eq!(res.unwrap(), TransactionImportResult::Current); - assert_eq!(miner.pending_transactions().len(), 1); - assert_eq!(miner.pending_transactions_hashes(best_block).len(), 0); - assert_eq!(miner.ready_transactions(best_block, 0).len(), 0); - assert_eq!(miner.pending_receipts(best_block).len(), 0); + assert_eq!(res.unwrap(), ()); + // By default we don't reseal on external transactions + assert_eq!(miner.pending_transactions(best_block), None); + assert_eq!(miner.pending_receipts(best_block), None); + // By default we use PendingSet::AlwaysSealing, so no transactions yet. + assert_eq!(miner.ready_transactions(&client, 10, PendingOrdering::Priority).len(), 0); // This method will let us know if pending block was created (before calling that method) - assert!(miner.prepare_work_sealing(&client)); + assert!(miner.prepare_pending_block(&client)); + // After pending block is created we should see a transaction. + assert_eq!(miner.ready_transactions(&client, 10, PendingOrdering::Priority).len(), 1); + } + + #[test] + fn should_treat_unfamiliar_locals_selectively() { + // given + let keypair = Random.generate().unwrap(); + let client = TestBlockChainClient::default(); + let account_provider = AccountProvider::transient_provider(); + account_provider.insert_account(keypair.secret().clone(), "").expect("can add accounts to the provider we just created"); + + let miner = Miner::new( + MinerOptions { + tx_queue_no_unfamiliar_locals: true, + ..miner().options + }, + GasPricer::new_fixed(0u64.into()), + &Spec::new_test(), + Some(Arc::new(account_provider)), + ); + let transaction = transaction(); + let best_block = 0; + // when + // This transaction should not be marked as local because our account_provider doesn't have the sender + let res = miner.import_claimed_local_transaction(&client, PendingTransaction::new(transaction.clone(), None), false); + + // then + // Check the same conditions as `should_import_external_transaction` first. Behaviour should be identical. + // That is: it's treated as though we added it through `import_external_transactions` + assert_eq!(res.unwrap(), ()); + assert_eq!(miner.pending_transactions(best_block), None); + assert_eq!(miner.pending_receipts(best_block), None); + assert_eq!(miner.ready_transactions(&client, 10, PendingOrdering::Priority).len(), 0); + assert!(miner.prepare_pending_block(&client)); + assert_eq!(miner.ready_transactions(&client, 10, PendingOrdering::Priority).len(), 1); + + // when - 2nd part: create a local transaction from account_provider. + // Borrow the transaction used before & sign with our generated keypair. + let local_transaction = transaction.deconstruct().0.as_unsigned().clone().sign(keypair.secret(), Some(TEST_CHAIN_ID)); + let res2 = miner.import_claimed_local_transaction(&client, PendingTransaction::new(local_transaction, None), false); + + // then - 2nd part: we add on the results from the last pending block. + // This is borrowed from `should_make_pending_block_when_importing_own_transaction` and slightly modified. + assert_eq!(res2.unwrap(), ()); + assert_eq!(miner.pending_transactions(best_block).unwrap().len(), 2); + assert_eq!(miner.pending_receipts(best_block).unwrap().len(), 2); + assert_eq!(miner.ready_transactions(&client, 10, PendingOrdering::Priority).len(), 2); + assert!(!miner.prepare_pending_block(&client)); } #[test] @@ -1472,7 +1334,7 @@ mod tests { assert!(!miner.requires_reseal(1u8.into())); miner.import_external_transactions(&client, vec![transaction().into()]).pop().unwrap().unwrap(); - assert!(miner.prepare_work_sealing(&client)); + assert!(miner.prepare_pending_block(&client)); // Unless asked to prepare work. assert!(miner.requires_reseal(1u8.into())); } @@ -1480,18 +1342,19 @@ mod tests { #[test] fn internal_seals_without_work() { let spec = Spec::new_instant(); - let miner = Miner::with_spec(&spec); + let miner = Miner::new_for_tests(&spec, None); let client = generate_dummy_client(2); - assert_eq!(miner.import_external_transactions(&*client, vec![transaction_with_chain_id(spec.chain_id()).into()]).pop().unwrap().unwrap(), TransactionImportResult::Current); + let import = miner.import_external_transactions(&*client, vec![transaction_with_chain_id(spec.chain_id()).into()]).pop().unwrap(); + assert_eq!(import.unwrap(), ()); miner.update_sealing(&*client); client.flush_queue(); assert!(miner.pending_block(0).is_none()); assert_eq!(client.chain_info().best_block_number, 3 as BlockNumber); - assert_eq!(miner.import_own_transaction(&*client, PendingTransaction::new(transaction_with_chain_id(spec.chain_id()).into(), None)).unwrap(), TransactionImportResult::Current); + assert!(miner.import_own_transaction(&*client, PendingTransaction::new(transaction_with_chain_id(spec.chain_id()).into(), None)).is_ok()); miner.update_sealing(&*client); client.flush_queue(); @@ -1499,21 +1362,63 @@ mod tests { assert_eq!(client.chain_info().best_block_number, 4 as BlockNumber); } - #[test] - fn should_fail_setting_engine_signer_on_pow() { - let spec = Spec::new_pow_test_spec; - let tap = Arc::new(AccountProvider::transient_provider()); - let addr = tap.insert_account(keccak("1").into(), "").unwrap(); - let client = generate_dummy_client_with_spec_and_accounts(spec, Some(tap.clone())); - assert!(match client.miner().set_engine_signer(addr, "".into()) { Err(AccountError::InappropriateChain) => true, _ => false }) - } - #[test] fn should_fail_setting_engine_signer_without_account_provider() { let spec = Spec::new_instant; let tap = Arc::new(AccountProvider::transient_provider()); let addr = tap.insert_account(keccak("1").into(), "").unwrap(); let client = generate_dummy_client_with_spec_and_accounts(spec, None); - assert!(match client.miner().set_engine_signer(addr, "".into()) { Err(AccountError::NotFound) => true, _ => false }); + assert!(match client.miner().set_author(addr, Some("".into())) { Err(AccountError::NotFound) => true, _ => false }); + } + + #[test] + fn should_mine_if_internal_sealing_is_enabled() { + let spec = Spec::new_instant(); + let miner = Miner::new_for_tests(&spec, None); + + let client = generate_dummy_client(2); + miner.update_sealing(&*client); + + assert!(miner.is_currently_sealing()); + } + + #[test] + fn should_not_mine_if_internal_sealing_is_disabled() { + let spec = Spec::new_test_round(); + let miner = Miner::new_for_tests(&spec, None); + + let client = generate_dummy_client(2); + miner.update_sealing(&*client); + + assert!(!miner.is_currently_sealing()); + } + + #[test] + fn should_not_mine_if_no_fetch_work_request() { + let spec = Spec::new_test(); + let miner = Miner::new_for_tests(&spec, None); + + let client = generate_dummy_client(2); + miner.update_sealing(&*client); + + assert!(!miner.is_currently_sealing()); + } + + #[test] + fn should_mine_if_fetch_work_request() { + struct DummyNotifyWork; + + impl NotifyWork for DummyNotifyWork { + fn notify(&self, _pow_hash: H256, _difficulty: U256, _number: u64) { } + } + + let spec = Spec::new_test(); + let miner = Miner::new_for_tests(&spec, None); + miner.add_work_listener(Box::new(DummyNotifyWork)); + + let client = generate_dummy_client(2); + miner.update_sealing(&*client); + + assert!(miner.is_currently_sealing()); } } diff --git a/ethcore/src/miner/mod.rs b/ethcore/src/miner/mod.rs index 5f451fdc2a630779342d1ea4098cfb027ff40659..1d7b9613bbdf3478430e9c991be4657e02b415ac 100644 --- a/ethcore/src/miner/mod.rs +++ b/ethcore/src/miner/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -17,191 +17,176 @@ #![warn(missing_docs)] //! Miner module -//! Keeps track of transactions and mined block. -//! -//! Usage example: -//! -//! ```rust -//! extern crate ethcore; -//! use std::env; -//! use ethcore::ethereum; -//! use ethcore::client::{Client, ClientConfig}; -//! use ethcore::miner::{Miner, MinerService}; -//! -//! fn main() { -//! let miner: Miner = Miner::with_spec(ðereum::new_foundation(&env::temp_dir())); -//! // get status -//! assert_eq!(miner.status().transactions_in_pending_queue, 0); -//! -//! // Check block for sealing -//! //assert!(miner.sealing_block(&*client).lock().is_some()); -//! } -//! ``` +//! Keeps track of transactions and currently sealed pending block. mod miner; -mod stratum; mod service_transaction_checker; -pub use self::miner::{Miner, MinerOptions, Banning, PendingSet, GasPricer, GasPriceCalibratorOptions, GasLimit}; -pub use self::stratum::{Stratum, Error as StratumError, Options as StratumOptions}; +pub mod pool_client; +pub mod stratum; -pub use ethcore_miner::local_transactions::Status as LocalTransactionStatus; +pub use self::miner::{Miner, MinerOptions, Penalization, PendingSet, AuthoringParams}; +pub use ethcore_miner::pool::PendingOrdering; +use std::sync::Arc; use std::collections::BTreeMap; -use block::{ClosedBlock, Block}; use bytes::Bytes; +use ethereum_types::{H256, U256, Address}; +use ethcore_miner::pool::{VerifiedTransaction, QueueStatus, local_transactions}; + +use block::{Block, SealedBlock}; use client::{ - MiningBlockChainClient, CallContract, RegistryInfo, ScheduleInfo, - BlockChain, AccountData, BlockProducer, SealedBlockImporter + CallContract, RegistryInfo, ScheduleInfo, + BlockChain, BlockProducer, SealedBlockImporter, ChainInfo, + AccountData, Nonce, }; -use error::{Error}; -use ethereum_types::{H256, U256, Address}; +use error::Error; use header::{BlockNumber, Header}; use receipt::{RichReceipt, Receipt}; -use transaction::{UnverifiedTransaction, PendingTransaction, ImportResult as TransactionImportResult}; +use transaction::{self, UnverifiedTransaction, SignedTransaction, PendingTransaction}; use state::StateInfo; +/// Provides methods to verify incoming external transactions +pub trait TransactionVerifierClient: Send + Sync + // Required for ServiceTransactionChecker + + CallContract + RegistryInfo + // Required for verifiying transactions + + BlockChain + ScheduleInfo + AccountData +{} + +/// Extended client interface used for mining +pub trait BlockChainClient: TransactionVerifierClient + BlockProducer + SealedBlockImporter {} + /// Miner client API pub trait MinerService : Send + Sync { /// Type representing chain state type State: StateInfo + 'static; - /// Returns miner's status. - fn status(&self) -> MinerStatus; - - /// Get the author that we will seal blocks as. - fn author(&self) -> Address; - - /// Set the author that we will seal blocks as. - fn set_author(&self, author: Address); - - /// Set info necessary to sign consensus messages. - fn set_engine_signer(&self, address: Address, password: String) -> Result<(), ::account_provider::SignError>; - - /// Get the extra_data that we will seal blocks with. - fn extra_data(&self) -> Bytes; - - /// Set the extra_data that we will seal blocks with. - fn set_extra_data(&self, extra_data: Bytes); - - /// Get current minimal gas price for transactions accepted to queue. - fn minimal_gas_price(&self) -> U256; - - /// Set minimal gas price of transaction to be accepted for mining. - fn set_minimal_gas_price(&self, min_gas_price: U256); + // Sealing - /// Get the lower bound of the gas limit we wish to target when sealing a new block. - fn gas_floor_target(&self) -> U256; + /// Submit `seal` as a valid solution for the header of `pow_hash`. + /// Will check the seal, but not actually insert the block into the chain. + fn submit_seal(&self, pow_hash: H256, seal: Vec) -> Result; - /// Get the upper bound of the gas limit we wish to target when sealing a new block. - fn gas_ceil_target(&self) -> U256; + /// Is it currently sealing? + fn is_currently_sealing(&self) -> bool; - // TODO: coalesce into single set_range function. - /// Set the lower bound of gas limit we wish to target when sealing a new block. - fn set_gas_floor_target(&self, target: U256); + /// Get the sealing work package preparing it if doesn't exist yet. + /// + /// Returns `None` if engine seals internally. + fn work_package(&self, chain: &C) -> Option<(H256, BlockNumber, u64, U256)> + where C: BlockChain + CallContract + BlockProducer + SealedBlockImporter + Nonce + Sync; - /// Set the upper bound of gas limit we wish to target when sealing a new block. - fn set_gas_ceil_target(&self, target: U256); + /// Update current pending block + fn update_sealing(&self, chain: &C) + where C: BlockChain + CallContract + BlockProducer + SealedBlockImporter + Nonce + Sync; - /// Get current transactions limit in queue. - fn transactions_limit(&self) -> usize; + // Notifications - /// Set maximal number of transactions kept in the queue (both current and future). - fn set_transactions_limit(&self, limit: usize); + /// Called when blocks are imported to chain, updates transactions queue. + /// `is_internal_import` indicates that the block has just been created in miner and internally sealed by the engine, + /// so we shouldn't attempt creating new block again. + fn chain_new_blocks(&self, chain: &C, imported: &[H256], invalid: &[H256], enacted: &[H256], retracted: &[H256], is_internal_import: bool) + where C: BlockChainClient; - /// Set maximum amount of gas allowed for any single transaction to mine. - fn set_tx_gas_limit(&self, limit: U256); + // Pending block - /// Imports transactions to transaction queue. - fn import_external_transactions(&self, client: &C, transactions: Vec) -> - Vec>; + /// Get a list of all pending receipts from pending block. + fn pending_receipts(&self, best_block: BlockNumber) -> Option>; - /// Imports own (node owner) transaction to queue. - fn import_own_transaction(&self, chain: &C, transaction: PendingTransaction) -> - Result; + /// Get a particular receipt from pending block. + fn pending_receipt(&self, best_block: BlockNumber, hash: &H256) -> Option; - /// Returns hashes of transactions currently in pending - fn pending_transactions_hashes(&self, best_block: BlockNumber) -> Vec; + /// Get `Some` `clone()` of the current pending block's state or `None` if we're not sealing. + fn pending_state(&self, latest_block_number: BlockNumber) -> Option; - /// Removes all transactions from the queue and restart mining operation. - fn clear_and_reset(&self, chain: &C); + /// Get `Some` `clone()` of the current pending block header or `None` if we're not sealing. + fn pending_block_header(&self, latest_block_number: BlockNumber) -> Option
; - /// Called when blocks are imported to chain, updates transactions queue. - fn chain_new_blocks(&self, chain: &C, imported: &[H256], invalid: &[H256], enacted: &[H256], retracted: &[H256]) - where C: AccountData + BlockChain + CallContract + RegistryInfo + BlockProducer + ScheduleInfo + SealedBlockImporter; + /// Get `Some` `clone()` of the current pending block or `None` if we're not sealing. + fn pending_block(&self, latest_block_number: BlockNumber) -> Option; - /// PoW chain - can produce work package - fn can_produce_work_package(&self) -> bool; + /// Get `Some` `clone()` of the current pending block transactions or `None` if we're not sealing. + fn pending_transactions(&self, latest_block_number: BlockNumber) -> Option>; - /// New chain head event. Restart mining operation. - fn update_sealing(&self, chain: &C) - where C: AccountData + BlockChain + RegistryInfo + CallContract + BlockProducer + SealedBlockImporter; + // Block authoring - /// Submit `seal` as a valid solution for the header of `pow_hash`. - /// Will check the seal, but not actually insert the block into the chain. - fn submit_seal(&self, chain: &C, pow_hash: H256, seal: Vec) -> Result<(), Error>; + /// Get current authoring parameters. + fn authoring_params(&self) -> AuthoringParams; - /// Get the sealing work package and if `Some`, apply some transform. - fn map_sealing_work(&self, client: &C, f: F) -> Option - where C: AccountData + BlockChain + BlockProducer + CallContract, - F: FnOnce(&ClosedBlock) -> T, - Self: Sized; + /// Set the lower and upper bound of gas limit we wish to target when sealing a new block. + fn set_gas_range_target(&self, gas_range_target: (U256, U256)); - /// Query pending transactions for hash. - fn transaction(&self, best_block: BlockNumber, hash: &H256) -> Option; + /// Set the extra_data that we will seal blocks with. + fn set_extra_data(&self, extra_data: Bytes); - /// Removes transaction from the queue. - /// NOTE: The transaction is not removed from pending block if mining. - fn remove_pending_transaction(&self, chain: &C, hash: &H256) -> Option; + /// Set info necessary to sign consensus messages and block authoring. + /// + /// On PoW password is optional. + fn set_author(&self, address: Address, password: Option) -> Result<(), ::account_provider::SignError>; - /// Get a list of all pending transactions in the queue. - fn pending_transactions(&self) -> Vec; + // Transaction Pool - /// Get a list of all transactions that can go into the given block. - fn ready_transactions(&self, best_block: BlockNumber, best_block_timestamp: u64) -> Vec; + /// Imports transactions to transaction queue. + fn import_external_transactions(&self, client: &C, transactions: Vec) + -> Vec> + where C: BlockChainClient; - /// Get a list of all future transactions. - fn future_transactions(&self) -> Vec; + /// Imports own (node owner) transaction to queue. + fn import_own_transaction(&self, chain: &C, transaction: PendingTransaction) + -> Result<(), transaction::Error> + where C: BlockChainClient; + + /// Imports transactions from potentially external sources, with behaviour determined + /// by the config flag `tx_queue_allow_unfamiliar_locals` + fn import_claimed_local_transaction(&self, chain: &C, transaction: PendingTransaction, trusted: bool) + -> Result<(), transaction::Error> + where C: BlockChainClient; + + /// Removes transaction from the pool. + /// + /// Attempts to "cancel" a transaction. If it was not propagated yet (or not accepted by other peers) + /// there is a good chance that the transaction will actually be removed. + /// NOTE: The transaction is not removed from pending block if there is one. + fn remove_transaction(&self, hash: &H256) -> Option>; + + /// Query transaction from the pool given it's hash. + fn transaction(&self, hash: &H256) -> Option>; + + /// Returns next valid nonce for given address. + /// + /// This includes nonces of all transactions from this address in the pending queue + /// if they are consecutive. + /// NOTE: pool may contain some future transactions that will become pending after + /// transaction with nonce returned from this function is signed on. + fn next_nonce(&self, chain: &C, address: &Address) -> U256 + where C: Nonce + Sync; + + /// Get a list of all ready transactions either ordered by priority or unordered (cheaper). + /// + /// Depending on the settings may look in transaction pool or only in pending block. + /// If you don't need a full set of transactions, you can add `max_len` and create only a limited set of + /// transactions. + fn ready_transactions(&self, chain: &C, max_len: usize, ordering: PendingOrdering) -> Vec> + where C: ChainInfo + Nonce + Sync; + + /// Get a list of all transactions in the pool (some of them might not be ready for inclusion yet). + fn queued_transactions(&self) -> Vec>; /// Get a list of local transactions with statuses. - fn local_transactions(&self) -> BTreeMap; - - /// Get a list of all pending receipts. - fn pending_receipts(&self, best_block: BlockNumber) -> BTreeMap; - - /// Get a particular reciept. - fn pending_receipt(&self, best_block: BlockNumber, hash: &H256) -> Option; + fn local_transactions(&self) -> BTreeMap; - /// Returns highest transaction nonce for given address. - fn last_nonce(&self, address: &Address) -> Option; + /// Get current queue status. + /// + /// Status includes verification thresholds and current pool utilization and limits. + fn queue_status(&self) -> QueueStatus; - /// Is it currently sealing? - fn is_currently_sealing(&self) -> bool; + // Misc /// Suggested gas price. fn sensible_gas_price(&self) -> U256; /// Suggested gas limit. - fn sensible_gas_limit(&self) -> U256 { 21000.into() } - - /// Get `Some` `clone()` of the current pending block's state or `None` if we're not sealing. - fn pending_state(&self, latest_block_number: BlockNumber) -> Option; - - /// Get `Some` `clone()` of the current pending block header or `None` if we're not sealing. - fn pending_block_header(&self, latest_block_number: BlockNumber) -> Option
; - - /// Get `Some` `clone()` of the current pending block or `None` if we're not sealing. - fn pending_block(&self, latest_block_number: BlockNumber) -> Option; -} - -/// Mining status -#[derive(Debug)] -pub struct MinerStatus { - /// Number of transactions in queue with state `pending` (ready to be included in block) - pub transactions_in_pending_queue: usize, - /// Number of transactions in queue with state `future` (not yet ready to be included in block) - pub transactions_in_future_queue: usize, - /// Number of transactions included in currently mined block - pub transactions_in_pending_block: usize, + fn sensible_gas_limit(&self) -> U256; } diff --git a/ethcore/src/miner/pool_client.rs b/ethcore/src/miner/pool_client.rs new file mode 100644 index 0000000000000000000000000000000000000000..bcf93d37532810359e6c6b1d0f170d5be52107a8 --- /dev/null +++ b/ethcore/src/miner/pool_client.rs @@ -0,0 +1,220 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Blockchain access for transaction pool. + +use std::fmt; +use std::collections::HashMap; + +use ethereum_types::{H256, U256, Address}; +use ethcore_miner::pool; +use ethcore_miner::pool::client::NonceClient; +use transaction::{ + self, + UnverifiedTransaction, + SignedTransaction, +}; +use parking_lot::RwLock; + +use account_provider::AccountProvider; +use client::{TransactionId, BlockInfo, CallContract, Nonce}; +use engines::EthEngine; +use header::Header; +use miner; +use miner::service_transaction_checker::ServiceTransactionChecker; + +type NoncesCache = RwLock>; + +const MAX_NONCE_CACHE_SIZE: usize = 4096; +const EXPECTED_NONCE_CACHE_SIZE: usize = 2048; + +/// Blockchain accesss for transaction pool. +pub struct PoolClient<'a, C: 'a> { + chain: &'a C, + cached_nonces: CachedNonceClient<'a, C>, + engine: &'a EthEngine, + accounts: Option<&'a AccountProvider>, + best_block_header: Header, + service_transaction_checker: Option, +} + +impl<'a, C: 'a> Clone for PoolClient<'a, C> { + fn clone(&self) -> Self { + PoolClient { + chain: self.chain, + cached_nonces: self.cached_nonces.clone(), + engine: self.engine, + accounts: self.accounts.clone(), + best_block_header: self.best_block_header.clone(), + service_transaction_checker: self.service_transaction_checker.clone(), + } + } +} + +impl<'a, C: 'a> PoolClient<'a, C> where +C: BlockInfo + CallContract, +{ + /// Creates new client given chain, nonce cache, accounts and service transaction verifier. + pub fn new( + chain: &'a C, + cache: &'a NoncesCache, + engine: &'a EthEngine, + accounts: Option<&'a AccountProvider>, + refuse_service_transactions: bool, + ) -> Self { + let best_block_header = chain.best_block_header(); + PoolClient { + chain, + cached_nonces: CachedNonceClient::new(chain, cache), + engine, + accounts, + best_block_header, + service_transaction_checker: if refuse_service_transactions { + None + } else { + Some(Default::default()) + }, + } + } + + /// Verifies if signed transaction is executable. + /// + /// This should perform any verifications that rely on chain status. + pub fn verify_signed(&self, tx: &SignedTransaction) -> Result<(), transaction::Error> { + self.engine.machine().verify_transaction(&tx, &self.best_block_header, self.chain) + } +} + +impl<'a, C: 'a> fmt::Debug for PoolClient<'a, C> { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "PoolClient") + } +} + +impl<'a, C: 'a> pool::client::Client for PoolClient<'a, C> where + C: miner::TransactionVerifierClient + Sync, +{ + fn transaction_already_included(&self, hash: &H256) -> bool { + self.chain.transaction_block(TransactionId::Hash(*hash)).is_some() + } + + fn verify_transaction(&self, tx: UnverifiedTransaction)-> Result { + self.engine.verify_transaction_basic(&tx, &self.best_block_header)?; + let tx = self.engine.verify_transaction_unordered(tx, &self.best_block_header)?; + + self.verify_signed(&tx)?; + + Ok(tx) + } + + fn account_details(&self, address: &Address) -> pool::client::AccountDetails { + pool::client::AccountDetails { + nonce: self.cached_nonces.account_nonce(address), + balance: self.chain.latest_balance(address), + is_local: self.accounts.map_or(false, |accounts| accounts.has_account(*address)), + } + } + + fn required_gas(&self, tx: &transaction::Transaction) -> U256 { + tx.gas_required(&self.chain.latest_schedule()).into() + } + + fn transaction_type(&self, tx: &SignedTransaction) -> pool::client::TransactionType { + match self.service_transaction_checker { + None => pool::client::TransactionType::Regular, + Some(ref checker) => match checker.check(self.chain, &tx) { + Ok(true) => pool::client::TransactionType::Service, + Ok(false) => pool::client::TransactionType::Regular, + Err(e) => { + debug!(target: "txqueue", "Unable to verify service transaction: {:?}", e); + pool::client::TransactionType::Regular + }, + } + } + } + + fn decode_transaction(&self, transaction: &[u8]) -> Result { + self.engine.decode_transaction(transaction) + } +} + +impl<'a, C: 'a> NonceClient for PoolClient<'a, C> where + C: Nonce + Sync, +{ + fn account_nonce(&self, address: &Address) -> U256 { + self.cached_nonces.account_nonce(address) + } +} + +pub(crate) struct CachedNonceClient<'a, C: 'a> { + client: &'a C, + cache: &'a NoncesCache, +} + +impl<'a, C: 'a> Clone for CachedNonceClient<'a, C> { + fn clone(&self) -> Self { + CachedNonceClient { + client: self.client, + cache: self.cache, + } + } +} + +impl<'a, C: 'a> fmt::Debug for CachedNonceClient<'a, C> { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_struct("CachedNonceClient") + .field("cache", &self.cache.read().len()) + .finish() + } +} + +impl<'a, C: 'a> CachedNonceClient<'a, C> { + pub fn new(client: &'a C, cache: &'a NoncesCache) -> Self { + CachedNonceClient { + client, + cache, + } + } +} + +impl<'a, C: 'a> NonceClient for CachedNonceClient<'a, C> where + C: Nonce + Sync, +{ + fn account_nonce(&self, address: &Address) -> U256 { + if let Some(nonce) = self.cache.read().get(address) { + return *nonce; + } + + // We don't check again if cache has been populated. + // It's not THAT expensive to fetch the nonce from state. + let mut cache = self.cache.write(); + let nonce = self.client.latest_nonce(address); + cache.insert(*address, nonce); + + if cache.len() < MAX_NONCE_CACHE_SIZE { + return nonce + } + + // Remove excessive amount of entries from the cache + while cache.len() > EXPECTED_NONCE_CACHE_SIZE { + // Just remove random entry + if let Some(key) = cache.keys().next().cloned() { + cache.remove(&key); + } + } + nonce + } +} diff --git a/ethcore/src/miner/service_transaction_checker.rs b/ethcore/src/miner/service_transaction_checker.rs index a555829c5f2d1fddb60ba90ec60cc5e9bb6bafb0..adae0c36ea8bd0352ae421dd59c5694af308f9d3 100644 --- a/ethcore/src/miner/service_transaction_checker.rs +++ b/ethcore/src/miner/service_transaction_checker.rs @@ -1,4 +1,4 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -12,37 +12,42 @@ // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity. If not, see . +// along with Parity. If not, see . //! A service transactions contract checker. -use client::{RegistryInfo, CallContract}; +use client::{RegistryInfo, CallContract, BlockId}; use transaction::SignedTransaction; -use types::ids::BlockId; use_contract!(service_transaction, "ServiceTransaction", "res/contracts/service_transaction.json"); const SERVICE_TRANSACTION_CONTRACT_REGISTRY_NAME: &'static str = "service_transaction_checker"; /// Service transactions checker. -#[derive(Default)] +#[derive(Default, Clone)] pub struct ServiceTransactionChecker { contract: service_transaction::ServiceTransaction, } impl ServiceTransactionChecker { - /// Checks if service transaction can be appended to the transaction queue. + /// Checks if given address is whitelisted to send service transactions. pub fn check(&self, client: &C, tx: &SignedTransaction) -> Result { - assert!(tx.gas_price.is_zero()); + let sender = tx.sender(); + let hash = tx.hash(); + + // Skip checking the contract if the transaction does not have zero gas price + if !tx.gas_price.is_zero() { + return Ok(false) + } let address = client.registry_address(SERVICE_TRANSACTION_CONTRACT_REGISTRY_NAME.to_owned(), BlockId::Latest) .ok_or_else(|| "contract is not configured")?; - trace!(target: "txqueue", "Checking service transaction checker contract from {}", address); + trace!(target: "txqueue", "[{:?}] Checking service transaction checker contract from {}", hash, sender); self.contract.functions() .certified() - .call(tx.sender(), &|data| client.call_contract(BlockId::Latest, address, data)) + .call(sender, &|data| client.call_contract(BlockId::Latest, address, data)) .map_err(|e| e.to_string()) } } diff --git a/ethcore/src/miner/stratum.rs b/ethcore/src/miner/stratum.rs index c8c387e51066912c25ee6badb373ccdd6059b656..0fd892bf50bcacf3f79b0671d8087c3d759a4600 100644 --- a/ethcore/src/miner/stratum.rs +++ b/ethcore/src/miner/stratum.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -20,8 +20,7 @@ use std::sync::{Arc, Weak}; use std::net::{SocketAddr, AddrParseError}; use std::fmt; -use block::IsBlock; -use client::Client; +use client::{Client, ImportSealedBlock}; use ethereum_types::{H64, H256, clean_0x, U256}; use ethereum::ethash::Ethash; use ethash::SeedHashCompute; @@ -30,7 +29,7 @@ use ethcore_stratum::{ JobDispatcher, PushWorkHandler, Stratum as StratumService, Error as StratumServiceError, }; -use miner::{self, Miner, MinerService}; +use miner::{Miner, MinerService}; use parking_lot::Mutex; use rlp::encode; @@ -112,7 +111,6 @@ pub struct StratumJobDispatcher { miner: Weak, } - impl JobDispatcher for StratumJobDispatcher { fn initial(&self) -> Option { // initial payload may contain additional data, not in this case @@ -120,14 +118,9 @@ impl JobDispatcher for StratumJobDispatcher { } fn job(&self) -> Option { - self.with_core(|client, miner| miner.map_sealing_work(&*client, |b| { - let pow_hash = b.hash(); - let number = b.block().header().number(); - let difficulty = b.block().header().difficulty(); - - self.payload(pow_hash, *difficulty, number) - }) - ) + self.with_core(|client, miner| miner.work_package(&*client).map(|(pow_hash, number, _timestamp, difficulty)| { + self.payload(pow_hash, difficulty, number) + })) } fn submit(&self, payload: Vec) -> Result<(), StratumServiceError> { @@ -145,7 +138,10 @@ impl JobDispatcher for StratumJobDispatcher { self.with_core_result(|client, miner| { let seal = vec![encode(&payload.mix_hash).into_vec(), encode(&payload.nonce).into_vec()]; - match miner.submit_seal(&*client, payload.pow_hash, seal) { + + let import = miner.submit_seal(payload.pow_hash, seal) + .and_then(|block| client.import_sealed_block(block)); + match import { Ok(_) => Ok(()), Err(e) => { warn!(target: "stratum", "submit_seal error: {:?}", e); @@ -247,8 +243,8 @@ impl Stratum { /// Start STRATUM job dispatcher and register it in the miner pub fn register(cfg: &Options, miner: Arc, client: Weak) -> Result<(), Error> { - let stratum = miner::Stratum::start(cfg, Arc::downgrade(&miner.clone()), client)?; - miner.push_notifier(Box::new(stratum) as Box); + let stratum = Stratum::start(cfg, Arc::downgrade(&miner.clone()), client)?; + miner.add_work_listener(Box::new(stratum) as Box); Ok(()) } } diff --git a/ethcore/src/pod_account.rs b/ethcore/src/pod_account.rs index 027e2765fff2074f6c9412e86e3869af584a5334..281299b3b2292ae515e6c675bbdd067f141daa01 100644 --- a/ethcore/src/pod_account.rs +++ b/ethcore/src/pod_account.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -165,7 +165,6 @@ pub fn diff_pod(pre: Option<&PodAccount>, post: Option<&PodAccount>) -> Option Result<(BasicAccount, Option), Error> { use trie::{TrieDBMut, TrieMut}; @@ -210,14 +210,14 @@ pub fn from_fat_rlp( mod tests { use account_db::{AccountDB, AccountDBMut}; use basic_account::BasicAccount; - use tests::helpers::get_temp_state_db; + use test_helpers::get_temp_state_db; use snapshot::tests::helpers::fill_storage; use hash::{KECCAK_EMPTY, KECCAK_NULL_RLP, keccak}; use ethereum_types::{H256, Address}; use hashdb::HashDB; use kvdb::DBValue; - use rlp::UntrustedRlp; + use rlp::Rlp; use std::collections::HashSet; @@ -236,10 +236,10 @@ mod tests { }; let thin_rlp = ::rlp::encode(&account); - assert_eq!(::rlp::decode::(&thin_rlp), account); + assert_eq!(::rlp::decode::(&thin_rlp).unwrap(), account); let fat_rlps = to_fat_rlps(&keccak(&addr), &account, &AccountDB::new(db.as_hashdb(), &addr), &mut Default::default(), usize::max_value(), usize::max_value()).unwrap(); - let fat_rlp = UntrustedRlp::new(&fat_rlps[0]).at(1).unwrap(); + let fat_rlp = Rlp::new(&fat_rlps[0]).at(1).unwrap(); assert_eq!(from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr), fat_rlp, H256::zero()).unwrap().0, account); } @@ -261,10 +261,10 @@ mod tests { }; let thin_rlp = ::rlp::encode(&account); - assert_eq!(::rlp::decode::(&thin_rlp), account); + assert_eq!(::rlp::decode::(&thin_rlp).unwrap(), account); let fat_rlp = to_fat_rlps(&keccak(&addr), &account, &AccountDB::new(db.as_hashdb(), &addr), &mut Default::default(), usize::max_value(), usize::max_value()).unwrap(); - let fat_rlp = UntrustedRlp::new(&fat_rlp[0]).at(1).unwrap(); + let fat_rlp = Rlp::new(&fat_rlp[0]).at(1).unwrap(); assert_eq!(from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr), fat_rlp, H256::zero()).unwrap().0, account); } @@ -286,13 +286,13 @@ mod tests { }; let thin_rlp = ::rlp::encode(&account); - assert_eq!(::rlp::decode::(&thin_rlp), account); + assert_eq!(::rlp::decode::(&thin_rlp).unwrap(), account); let fat_rlps = to_fat_rlps(&keccak(addr), &account, &AccountDB::new(db.as_hashdb(), &addr), &mut Default::default(), 500, 1000).unwrap(); let mut root = KECCAK_NULL_RLP; let mut restored_account = None; for rlp in fat_rlps { - let fat_rlp = UntrustedRlp::new(&rlp).at(1).unwrap(); + let fat_rlp = Rlp::new(&rlp).at(1).unwrap(); restored_account = Some(from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr), fat_rlp, root).unwrap().0); root = restored_account.as_ref().unwrap().storage_root.clone(); } @@ -336,8 +336,8 @@ mod tests { let fat_rlp2 = to_fat_rlps(&keccak(&addr2), &account2, &AccountDB::new(db.as_hashdb(), &addr2), &mut used_code, usize::max_value(), usize::max_value()).unwrap(); assert_eq!(used_code.len(), 1); - let fat_rlp1 = UntrustedRlp::new(&fat_rlp1[0]).at(1).unwrap(); - let fat_rlp2 = UntrustedRlp::new(&fat_rlp2[0]).at(1).unwrap(); + let fat_rlp1 = Rlp::new(&fat_rlp1[0]).at(1).unwrap(); + let fat_rlp2 = Rlp::new(&fat_rlp2[0]).at(1).unwrap(); let (acc, maybe_code) = from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr2), fat_rlp2, H256::zero()).unwrap(); assert!(maybe_code.is_none()); @@ -351,6 +351,6 @@ mod tests { #[test] fn encoding_empty_acc() { let mut db = get_temp_state_db(); - assert_eq!(from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &Address::default()), UntrustedRlp::new(&::rlp::NULL_RLP), H256::zero()).unwrap(), (ACC_EMPTY, None)); + assert_eq!(from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &Address::default()), Rlp::new(&::rlp::NULL_RLP), H256::zero()).unwrap(), (ACC_EMPTY, None)); } } diff --git a/ethcore/src/snapshot/block.rs b/ethcore/src/snapshot/block.rs index 98215b3244d9f1bca80252e6792181cf21892688..cec2e491a702774dea21c62c62dffbb71cbd72f5 100644 --- a/ethcore/src/snapshot/block.rs +++ b/ethcore/src/snapshot/block.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -21,7 +21,7 @@ use header::Header; use hash::keccak; use views::BlockView; -use rlp::{DecoderError, RlpStream, UntrustedRlp}; +use rlp::{DecoderError, RlpStream, Rlp}; use ethereum_types::H256; use bytes::Bytes; use triehash::ordered_trie_root; @@ -89,7 +89,7 @@ impl AbridgedBlock { /// /// Will fail if contains invalid rlp. pub fn to_block(&self, parent_hash: H256, number: u64, receipts_root: H256) -> Result { - let rlp = UntrustedRlp::new(&self.rlp); + let rlp = Rlp::new(&self.rlp); let mut header: Header = Default::default(); header.set_parent_hash(parent_hash); @@ -135,7 +135,6 @@ impl AbridgedBlock { mod tests { use views::BlockView; use block::Block; - use header::Seal; use super::AbridgedBlock; use transaction::{Action, Transaction}; @@ -143,7 +142,7 @@ mod tests { use bytes::Bytes; fn encode_block(b: &Block) -> Bytes { - b.rlp_bytes(Seal::With) + b.rlp_bytes() } #[test] @@ -152,7 +151,7 @@ mod tests { let receipts_root = b.header.receipts_root().clone(); let encoded = encode_block(&b); - let abridged = AbridgedBlock::from_block_view(&BlockView::new(&encoded)); + let abridged = AbridgedBlock::from_block_view(&view!(BlockView, &encoded)); assert_eq!(abridged.to_block(H256::new(), 0, receipts_root).unwrap(), b); } @@ -163,7 +162,7 @@ mod tests { let receipts_root = b.header.receipts_root().clone(); let encoded = encode_block(&b); - let abridged = AbridgedBlock::from_block_view(&BlockView::new(&encoded)); + let abridged = AbridgedBlock::from_block_view(&view!(BlockView, &encoded)); assert_eq!(abridged.to_block(H256::new(), 2, receipts_root).unwrap(), b); } @@ -199,7 +198,7 @@ mod tests { let encoded = encode_block(&b); - let abridged = AbridgedBlock::from_block_view(&BlockView::new(&encoded[..])); + let abridged = AbridgedBlock::from_block_view(&view!(BlockView, &encoded[..])); assert_eq!(abridged.to_block(H256::new(), 0, receipts_root).unwrap(), b); } } diff --git a/ethcore/src/snapshot/consensus/authority.rs b/ethcore/src/snapshot/consensus/authority.rs index 46f433c00503e8992179758a1d2a73fa7787e0c7..0bb6d2329552552c5386f2caa60cff50dcfc8e44 100644 --- a/ethcore/src/snapshot/consensus/authority.rs +++ b/ethcore/src/snapshot/consensus/authority.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -28,12 +28,12 @@ use blockchain::{BlockChain, BlockProvider}; use engines::{EthEngine, EpochVerifier, EpochTransition}; use machine::EthereumMachine; use ids::BlockId; -use header::{Header, Seal}; +use header::Header; use receipt::Receipt; use snapshot::{Error, ManifestData}; use itertools::{Position, Itertools}; -use rlp::{RlpStream, UntrustedRlp}; +use rlp::{RlpStream, Rlp}; use ethereum_types::{H256, U256}; use kvdb::KeyValueDB; use bytes::Bytes; @@ -100,7 +100,7 @@ impl SnapshotComponents for PoaSnapshot { let (block, receipts) = chain.block(&block_at) .and_then(|b| chain.block_receipts(&block_at).map(|r| (b, r))) .ok_or(Error::BlockNotFound(block_at))?; - let block = block.decode(); + let block = block.decode()?; let parent_td = chain.block_details(block.header.parent_hash()) .map(|d| d.total_difficulty) @@ -182,7 +182,7 @@ impl ChunkRebuilder { fn verify_transition( &mut self, last_verifier: &mut Option>>, - transition_rlp: UntrustedRlp, + transition_rlp: Rlp, engine: &EthEngine, ) -> Result { use engines::ConstructedVerifier; @@ -242,7 +242,7 @@ impl Rebuilder for ChunkRebuilder { engine: &EthEngine, abort_flag: &AtomicBool, ) -> Result<(), ::error::Error> { - let rlp = UntrustedRlp::new(chunk); + let rlp = Rlp::new(chunk); let is_last_chunk: bool = rlp.val_at(0)?; let num_items = rlp.item_count()?; @@ -324,7 +324,7 @@ impl Rebuilder for ChunkRebuilder { transactions: last_rlp.list_at(1)?, uncles: last_rlp.list_at(2)?, }; - let block_data = block.rlp_bytes(Seal::With); + let block_data = block.rlp_bytes(); let receipts: Vec = last_rlp.list_at(3)?; { diff --git a/ethcore/src/snapshot/consensus/mod.rs b/ethcore/src/snapshot/consensus/mod.rs index 712c245ff37d1ca1cf4f763c3c1e971dc3dc24cb..8c8ce3776f1e7458bb6575f1c8f96f02d0956134 100644 --- a/ethcore/src/snapshot/consensus/mod.rs +++ b/ethcore/src/snapshot/consensus/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -74,7 +74,6 @@ pub trait SnapshotComponents: Send { fn current_version(&self) -> u64; } - /// Restore from secondary snapshot chunks. pub trait Rebuilder: Send { /// Feed a chunk, potentially out of order. diff --git a/ethcore/src/snapshot/consensus/work.rs b/ethcore/src/snapshot/consensus/work.rs index 5b4ff4888935501fb71f54632e1349b33f682954..31c7b51ec5564cce7e01b90a4dc2e7b627f193a1 100644 --- a/ethcore/src/snapshot/consensus/work.rs +++ b/ethcore/src/snapshot/consensus/work.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -33,7 +33,7 @@ use snapshot::block::AbridgedBlock; use ethereum_types::H256; use kvdb::KeyValueDB; use bytes::Bytes; -use rlp::{RlpStream, UntrustedRlp}; +use rlp::{RlpStream, Rlp}; use rand::OsRng; /// Snapshot creation and restoration for PoW chains. @@ -153,19 +153,19 @@ impl<'a> PowWorker<'a> { fn write_chunk(&mut self, last: H256) -> Result<(), Error> { trace!(target: "snapshot", "prepared block chunk with {} blocks", self.rlps.len()); - let (last_header, last_details) = self.chain.block_header(&last) + let (last_header, last_details) = self.chain.block_header_data(&last) .and_then(|n| self.chain.block_details(&last).map(|d| (n, d))) .ok_or(Error::BlockNotFound(last))?; let parent_number = last_header.number() - 1; let parent_hash = last_header.parent_hash(); - let parent_total_difficulty = last_details.total_difficulty - *last_header.difficulty(); + let parent_total_difficulty = last_details.total_difficulty - last_header.difficulty(); trace!(target: "snapshot", "parent last written block: {}", parent_hash); let num_entries = self.rlps.len(); let mut rlp_stream = RlpStream::new_list(3 + num_entries); - rlp_stream.append(&parent_number).append(parent_hash).append(&parent_total_difficulty); + rlp_stream.append(&parent_number).append(&parent_hash).append(&parent_total_difficulty); for pair in self.rlps.drain(..) { rlp_stream.append_raw(&pair, 1); @@ -220,17 +220,16 @@ impl Rebuilder for PowRebuilder { /// Feed the rebuilder an uncompressed block chunk. /// Returns the number of blocks fed or any errors. fn feed(&mut self, chunk: &[u8], engine: &EthEngine, abort_flag: &AtomicBool) -> Result<(), ::error::Error> { - use header::Seal; use views::BlockView; use snapshot::verify_old_block; use ethereum_types::U256; use triehash::ordered_trie_root; - let rlp = UntrustedRlp::new(chunk); + let rlp = Rlp::new(chunk); let item_count = rlp.item_count()?; let num_blocks = (item_count - 3) as u64; - trace!(target: "snapshot", "restoring block chunk with {} blocks.", item_count - 3); + trace!(target: "snapshot", "restoring block chunk with {} blocks.", num_blocks); if self.fed_blocks + num_blocks > self.snapshot_blocks { return Err(Error::TooManyBlocks(self.snapshot_blocks, self.fed_blocks + num_blocks).into()) @@ -251,7 +250,7 @@ impl Rebuilder for PowRebuilder { let receipts_root = ordered_trie_root(pair.at(1)?.iter().map(|r| r.as_raw())); let block = abridged_block.to_block(parent_hash, cur_number, receipts_root)?; - let block_bytes = block.rlp_bytes(Seal::With); + let block_bytes = block.rlp_bytes(); let is_best = cur_number == self.best_number; if is_best { @@ -285,7 +284,7 @@ impl Rebuilder for PowRebuilder { self.db.write_buffered(batch); self.chain.commit(); - parent_hash = BlockView::new(&block_bytes).hash(); + parent_hash = view!(BlockView, &block_bytes).hash(); cur_number += 1; } diff --git a/ethcore/src/snapshot/error.rs b/ethcore/src/snapshot/error.rs index 2741f648a2b917f65ce602c7f53725d61c611fc4..36fb0927a6bc636033f724d33503f41bd26a8920 100644 --- a/ethcore/src/snapshot/error.rs +++ b/ethcore/src/snapshot/error.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/src/snapshot/io.rs b/ethcore/src/snapshot/io.rs index 7e38177ea526f955cd080ab63fee5583a3a679dd..7d2cbcf92cf5d1a9def5b7082db038f3247aec27 100644 --- a/ethcore/src/snapshot/io.rs +++ b/ethcore/src/snapshot/io.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -27,7 +27,7 @@ use std::path::{Path, PathBuf}; use bytes::Bytes; use ethereum_types::H256; -use rlp::{RlpStream, UntrustedRlp}; +use rlp::{RlpStream, Rlp}; use super::ManifestData; @@ -214,7 +214,6 @@ impl PackedReader { return Ok(None); } - file.seek(SeekFrom::End(-8))?; let mut off_bytes = [0u8; 8]; @@ -238,7 +237,7 @@ impl PackedReader { file.seek(SeekFrom::Start(manifest_off))?; file.read_exact(&mut manifest_buf)?; - let rlp = UntrustedRlp::new(&manifest_buf); + let rlp = Rlp::new(&manifest_buf); let (start, version) = if rlp.item_count()? == 5 { (0, 1) diff --git a/ethcore/src/snapshot/mod.rs b/ethcore/src/snapshot/mod.rs index f9a513dd17868c1a0d025dc64ed19f5e1478c873..30a61b779cc234d3c141e119b8f393cf573ae6d0 100644 --- a/ethcore/src/snapshot/mod.rs +++ b/ethcore/src/snapshot/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -39,7 +39,7 @@ use parking_lot::Mutex; use journaldb::{self, Algorithm, JournalDB}; use kvdb::KeyValueDB; use trie::{TrieDB, TrieDBMut, Trie, TrieMut}; -use rlp::{RlpStream, UntrustedRlp}; +use rlp::{RlpStream, Rlp}; use bloom_journal::Bloom; use self::io::SnapshotWriter; @@ -130,7 +130,7 @@ pub fn take_snapshot( writer: W, p: &Progress ) -> Result<(), Error> { - let start_header = chain.block_header(&block_at) + let start_header = chain.block_header_data(&block_at) .ok_or(Error::InvalidStartingBlock(BlockId::Hash(block_at)))?; let state_root = start_header.state_root(); let number = start_header.number(); @@ -143,7 +143,7 @@ pub fn take_snapshot( let (state_hashes, block_hashes) = scope(|scope| { let writer = &writer; let block_guard = scope.spawn(move || chunk_secondary(chunker, chain, block_at, writer, p)); - let state_res = chunk_state(state_db, state_root, writer, p); + let state_res = chunk_state(state_db, &state_root, writer, p); state_res.and_then(|state_hashes| { block_guard.join().map(|block_hashes| (state_hashes, block_hashes)) @@ -156,7 +156,7 @@ pub fn take_snapshot( version: snapshot_version, state_hashes: state_hashes, block_hashes: block_hashes, - state_root: *state_root, + state_root: state_root, block_number: number, block_hash: block_at, }; @@ -281,7 +281,7 @@ pub fn chunk_state<'a>(db: &HashDB, root: &H256, writer: &Mutex Result<(), ::error::Error> { - let rlp = UntrustedRlp::new(chunk); + let rlp = Rlp::new(chunk); let empty_rlp = StateAccount::new_basic(U256::zero(), U256::zero()).rlp(); let mut pairs = Vec::with_capacity(rlp.item_count()?); @@ -415,7 +415,7 @@ struct RebuiltStatus { // returns a status detailing newly-loaded code and accounts missing code. fn rebuild_accounts( db: &mut HashDB, - account_fat_rlps: UntrustedRlp, + account_fat_rlps: Rlp, out_chunk: &mut [(H256, Bytes)], known_code: &HashMap, known_storage_roots: &mut HashMap, @@ -467,10 +467,10 @@ fn rebuild_accounts( *out = (hash, thin_rlp); } if let Some(&(ref hash, ref rlp)) = out_chunk.iter().last() { - known_storage_roots.insert(*hash, ::rlp::decode::(rlp).storage_root); + known_storage_roots.insert(*hash, ::rlp::decode::(rlp)?.storage_root); } if let Some(&(ref hash, ref rlp)) = out_chunk.iter().next() { - known_storage_roots.insert(*hash, ::rlp::decode::(rlp).storage_root); + known_storage_roots.insert(*hash, ::rlp::decode::(rlp)?.storage_root); } Ok(status) } @@ -486,8 +486,8 @@ pub fn verify_old_block(rng: &mut OsRng, header: &Header, engine: &EthEngine, ch if always || rng.gen::() <= POW_VERIFY_RATE { engine.verify_block_unordered(header)?; - match chain.block_header(header.parent_hash()) { - Some(parent) => engine.verify_block_family(header, &parent), + match chain.block_header_data(header.parent_hash()) { + Some(parent) => engine.verify_block_family(header, &parent.decode()?), None => Ok(()), } } else { diff --git a/ethcore/src/snapshot/service.rs b/ethcore/src/snapshot/service.rs index 7def518f35723b207ce314b74f311a6d915f69a4..b76a703675281a8b351d75e42355afb46f763fa8 100644 --- a/ethcore/src/snapshot/service.rs +++ b/ethcore/src/snapshot/service.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -17,8 +17,8 @@ //! Snapshot network service implementation. use std::collections::HashSet; -use std::io::ErrorKind; -use std::fs; +use std::io::{self, Read, ErrorKind}; +use std::fs::{self, File}; use std::path::PathBuf; use std::sync::Arc; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; @@ -30,6 +30,7 @@ use blockchain::BlockChain; use client::{Client, ChainInfo, ClientIoMessage}; use engines::EthEngine; use error::Error; +use hash::keccak; use ids::BlockId; use io::IoChannel; @@ -39,7 +40,7 @@ use parking_lot::{Mutex, RwLock, RwLockReadGuard}; use util_error::UtilError; use bytes::Bytes; use journaldb::Algorithm; -use kvdb_rocksdb::{Database, DatabaseConfig}; +use kvdb::{KeyValueDB, KeyValueDBHandler}; use snappy; /// Helper for removing directories in case of error. @@ -79,14 +80,13 @@ struct Restoration { snappy_buffer: Bytes, final_state_root: H256, guard: Guard, - db: Arc, + db: Arc, } struct RestorationParams<'a> { manifest: ManifestData, // manifest to base restoration on. pruning: Algorithm, // pruning algorithm for the database. - db_path: PathBuf, // database path - db_config: &'a DatabaseConfig, // configuration for the database. + db: Arc, // database writer: Option, // writer for recovered snapshot. genesis: &'a [u8], // genesis block of the chain. guard: Guard, // guard for the restoration directory. @@ -101,8 +101,7 @@ impl Restoration { let state_chunks = manifest.state_hashes.iter().cloned().collect(); let block_chunks = manifest.block_hashes.iter().cloned().collect(); - let raw_db = Arc::new(Database::open(params.db_config, &*params.db_path.to_string_lossy()) - .map_err(UtilError::from)?); + let raw_db = params.db; let chain = BlockChain::new(Default::default(), params.genesis, raw_db.clone()); let components = params.engine.snapshot_components() @@ -178,7 +177,7 @@ impl Restoration { // verify final state root. let root = self.state.state_root(); if root != self.final_state_root { - warn!("Final restored state has wrong state root: expected {:?}, got {:?}", root, self.final_state_root); + warn!("Final restored state has wrong state root: expected {:?}, got {:?}", self.final_state_root, root); return Err(TrieError::InvalidStateRoot(root).into()); } @@ -211,10 +210,10 @@ pub struct ServiceParams { pub engine: Arc, /// The chain's genesis block. pub genesis_block: Bytes, - /// Database configuration options. - pub db_config: DatabaseConfig, /// State pruning algorithm. pub pruning: Algorithm, + /// Handler for opening a restoration DB. + pub restoration_db_handler: Box, /// Async IO channel for sending messages. pub channel: Channel, /// The directory to put snapshots in. @@ -228,8 +227,8 @@ pub struct ServiceParams { /// This controls taking snapshots and restoring from them. pub struct Service { restoration: Mutex>, + restoration_db_handler: Box, snapshot_root: PathBuf, - db_config: DatabaseConfig, io_channel: Mutex, pruning: Algorithm, status: Mutex, @@ -249,8 +248,8 @@ impl Service { pub fn new(params: ServiceParams) -> Result { let mut service = Service { restoration: Mutex::new(None), + restoration_db_handler: params.restoration_db_handler, snapshot_root: params.snapshot_root, - db_config: params.db_config, io_channel: Mutex::new(params.channel), pruning: params.pruning, status: Mutex::new(RestorationStatus::Inactive), @@ -272,8 +271,8 @@ impl Service { } } - // delete the temporary restoration dir if it does exist. - if let Err(e) = fs::remove_dir_all(service.restoration_dir()) { + // delete the temporary restoration DB dir if it does exist. + if let Err(e) = fs::remove_dir_all(service.restoration_db()) { if e.kind() != ErrorKind::NotFound { return Err(e.into()) } @@ -327,6 +326,13 @@ impl Service { dir } + // previous snapshot chunks path. + fn prev_chunks_dir(&self) -> PathBuf { + let mut dir = self.snapshot_root.clone(); + dir.push("prev_chunks"); + dir + } + // replace one the client's database with our own. fn replace_client_db(&self) -> Result<(), Error> { let our_db = self.restoration_db(); @@ -408,9 +414,26 @@ impl Service { /// Initialize the restoration synchronously. /// The recover flag indicates whether to recover the restored snapshot. pub fn init_restore(&self, manifest: ManifestData, recover: bool) -> Result<(), Error> { + let mut res = self.restoration.lock(); + let rest_dir = self.restoration_dir(); + let rest_db = self.restoration_db(); + let recovery_temp = self.temp_recovery_dir(); + let prev_chunks = self.prev_chunks_dir(); - let mut res = self.restoration.lock(); + // delete and restore the restoration dir. + if let Err(e) = fs::remove_dir_all(&prev_chunks) { + match e.kind() { + ErrorKind::NotFound => {}, + _ => return Err(e.into()), + } + } + + // Move the previous recovery temp directory + // to `prev_chunks` to be able to restart restoring + // with previously downloaded blocks + // This step is optional, so don't fail on error + fs::rename(&recovery_temp, &prev_chunks).ok(); self.state_chunks.store(0, Ordering::SeqCst); self.block_chunks.store(0, Ordering::SeqCst); @@ -426,30 +449,38 @@ impl Service { } } + *self.status.lock() = RestorationStatus::Initializing { + chunks_done: 0, + }; + fs::create_dir_all(&rest_dir)?; // make new restoration. let writer = match recover { - true => Some(LooseWriter::new(self.temp_recovery_dir())?), + true => Some(LooseWriter::new(recovery_temp)?), false => None }; let params = RestorationParams { - manifest: manifest, + manifest: manifest.clone(), pruning: self.pruning, - db_path: self.restoration_db(), - db_config: &self.db_config, + db: self.restoration_db_handler.open(&rest_db)?, writer: writer, genesis: &self.genesis_block, - guard: Guard::new(rest_dir), + guard: Guard::new(rest_db), engine: &*self.engine, }; - let state_chunks = params.manifest.state_hashes.len(); - let block_chunks = params.manifest.block_hashes.len(); + let state_chunks = manifest.state_hashes.len(); + let block_chunks = manifest.block_hashes.len(); *res = Some(Restoration::new(params)?); + self.restoring_snapshot.store(true, Ordering::SeqCst); + + // Import previous chunks, continue if it fails + self.import_prev_chunks(&mut res, manifest).ok(); + *self.status.lock() = RestorationStatus::Ongoing { state_chunks: state_chunks as u32, block_chunks: block_chunks as u32, @@ -457,10 +488,65 @@ impl Service { block_chunks_done: self.block_chunks.load(Ordering::SeqCst) as u32, }; - self.restoring_snapshot.store(true, Ordering::SeqCst); Ok(()) } + /// Import the previous chunks into the current restoration + fn import_prev_chunks(&self, restoration: &mut Option, manifest: ManifestData) -> Result<(), Error> { + let prev_chunks = self.prev_chunks_dir(); + + // Restore previous snapshot chunks + let files = fs::read_dir(prev_chunks.as_path())?; + let mut num_temp_chunks = 0; + + for prev_chunk_file in files { + // Don't go over all the files if the restoration has been aborted + if !self.restoring_snapshot.load(Ordering::SeqCst) { + trace!(target:"snapshot", "Aborting importing previous chunks"); + return Ok(()); + } + // Import the chunk, don't fail and continue if one fails + match self.import_prev_chunk(restoration, &manifest, prev_chunk_file) { + Ok(true) => num_temp_chunks += 1, + Err(e) => trace!(target: "snapshot", "Error importing chunk: {:?}", e), + _ => (), + } + } + + trace!(target:"snapshot", "Imported {} previous chunks", num_temp_chunks); + + // Remove the prev temp directory + fs::remove_dir_all(&prev_chunks)?; + + Ok(()) + } + + /// Import a previous chunk at the given path. Returns whether the block was imported or not + fn import_prev_chunk(&self, restoration: &mut Option, manifest: &ManifestData, file: io::Result) -> Result { + let file = file?; + let path = file.path(); + + let mut file = File::open(path.clone())?; + let mut buffer = Vec::new(); + file.read_to_end(&mut buffer)?; + + let hash = keccak(&buffer); + + let is_state = if manifest.block_hashes.contains(&hash) { + false + } else if manifest.state_hashes.contains(&hash) { + true + } else { + return Ok(false); + }; + + self.feed_chunk_with_restoration(restoration, hash, &buffer, is_state)?; + + trace!(target: "snapshot", "Fed chunk {:?}", hash); + + Ok(true) + } + // finalize the restoration. this accepts an already-locked // restoration as an argument -- so acquiring it again _will_ // lead to deadlock. @@ -502,12 +588,19 @@ impl Service { /// Feed a chunk of either kind. no-op if no restoration or status is wrong. fn feed_chunk(&self, hash: H256, chunk: &[u8], is_state: bool) -> Result<(), Error> { // TODO: be able to process block chunks and state chunks at same time? - let (result, db) = { - let mut restoration = self.restoration.lock(); + let mut restoration = self.restoration.lock(); + self.feed_chunk_with_restoration(&mut restoration, hash, chunk, is_state) + } + /// Feed a chunk with the Restoration + fn feed_chunk_with_restoration(&self, restoration: &mut Option, hash: H256, chunk: &[u8], is_state: bool) -> Result<(), Error> { + let (result, db) = { match self.status() { - RestorationStatus::Inactive | RestorationStatus::Failed => return Ok(()), - RestorationStatus::Ongoing { .. } => { + RestorationStatus::Inactive | RestorationStatus::Failed => { + trace!(target: "snapshot", "Tried to restore chunk {:x} while inactive or failed", hash); + return Ok(()); + }, + RestorationStatus::Ongoing { .. } | RestorationStatus::Initializing { .. } => { let (res, db) = { let rest = match *restoration { Some(ref mut r) => r, @@ -586,11 +679,41 @@ impl SnapshotService for Service { self.reader.read().as_ref().and_then(|r| r.chunk(hash).ok()) } + fn completed_chunks(&self) -> Option> { + let restoration = self.restoration.lock(); + + match *restoration { + Some(ref restoration) => { + let completed_chunks = restoration.manifest.block_hashes + .iter() + .filter(|h| !restoration.block_chunks_left.contains(h)) + .chain( + restoration.manifest.state_hashes + .iter() + .filter(|h| !restoration.state_chunks_left.contains(h)) + ) + .map(|h| *h) + .collect(); + + Some(completed_chunks) + }, + None => None, + } + } + fn status(&self) -> RestorationStatus { let mut cur_status = self.status.lock(); - if let RestorationStatus::Ongoing { ref mut state_chunks_done, ref mut block_chunks_done, .. } = *cur_status { - *state_chunks_done = self.state_chunks.load(Ordering::SeqCst) as u32; - *block_chunks_done = self.block_chunks.load(Ordering::SeqCst) as u32; + + match *cur_status { + RestorationStatus::Initializing { ref mut chunks_done } => { + *chunks_done = self.state_chunks.load(Ordering::SeqCst) as u32 + + self.block_chunks.load(Ordering::SeqCst) as u32; + } + RestorationStatus::Ongoing { ref mut state_chunks_done, ref mut block_chunks_done, .. } => { + *state_chunks_done = self.state_chunks.load(Ordering::SeqCst) as u32; + *block_chunks_done = self.block_chunks.load(Ordering::SeqCst) as u32; + }, + _ => (), } cur_status.clone() @@ -603,6 +726,7 @@ impl SnapshotService for Service { } fn abort_restore(&self) { + trace!(target: "snapshot", "Aborting restore"); self.restoring_snapshot.store(false, Ordering::SeqCst); *self.restoration.lock() = None; *self.status.lock() = RestorationStatus::Inactive; @@ -619,6 +743,10 @@ impl SnapshotService for Service { trace!("Error sending snapshot service message: {:?}", e); } } + + fn shutdown(&self) { + self.abort_restore(); + } } impl Drop for Service { @@ -638,6 +766,7 @@ mod tests { use snapshot::{ManifestData, RestorationStatus, SnapshotService}; use super::*; use tempdir::TempDir; + use test_helpers_internal::restoration_db_handler; struct NoopDBRestore; impl DatabaseRestore for NoopDBRestore { @@ -657,7 +786,7 @@ mod tests { let snapshot_params = ServiceParams { engine: spec.engine.clone(), genesis_block: spec.genesis_block(), - db_config: Default::default(), + restoration_db_handler: restoration_db_handler(Default::default()), pruning: Algorithm::Archive, channel: service.channel(), snapshot_root: dir, @@ -709,8 +838,7 @@ mod tests { block_hash: H256::default(), }, pruning: Algorithm::Archive, - db_path: tempdir.path().to_owned(), - db_config: &db_config, + db: restoration_db_handler(db_config).open(&tempdir.path().to_owned()).unwrap(), writer: None, genesis: &gb, guard: Guard::benign(), diff --git a/ethcore/src/snapshot/tests/helpers.rs b/ethcore/src/snapshot/tests/helpers.rs index 51f417149bf7ac7e09407227b0e0f37cb6a3f5a8..516e438abdb27be1fc2a3e19ed25e0efe82d0ec2 100644 --- a/ethcore/src/snapshot/tests/helpers.rs +++ b/ethcore/src/snapshot/tests/helpers.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -75,7 +75,7 @@ impl StateProducer { // sweep once to alter storage tries. for &mut (ref mut address_hash, ref mut account_data) in &mut accounts_to_modify { - let mut account: BasicAccount = ::rlp::decode(&*account_data); + let mut account: BasicAccount = ::rlp::decode(&*account_data).expect("error decoding basic account"); let acct_db = AccountDBMut::from_hash(db, *address_hash); fill_storage(acct_db, &mut account.storage_root, &mut self.storage_seed); *account_data = DBValue::from_vec(::rlp::encode(&account).into_vec()); diff --git a/ethcore/src/snapshot/tests/mod.rs b/ethcore/src/snapshot/tests/mod.rs index 6e9398356a02745ace4448072de4979996409bc2..c09f2b965c71ad9d7c3e9ea05f8df50ea2967b9f 100644 --- a/ethcore/src/snapshot/tests/mod.rs +++ b/ethcore/src/snapshot/tests/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/src/snapshot/tests/proof_of_authority.rs b/ethcore/src/snapshot/tests/proof_of_authority.rs index 101de5c586cbf6e4210d06d2d5bd758da82480c7..04fe7c4c6831dd386ede5ac1a0200a74efe3f712 100644 --- a/ethcore/src/snapshot/tests/proof_of_authority.rs +++ b/ethcore/src/snapshot/tests/proof_of_authority.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -25,7 +25,7 @@ use client::{Client, BlockChainClient, ChainInfo}; use ethkey::Secret; use snapshot::tests::helpers as snapshot_helpers; use spec::Spec; -use tests::helpers; +use test_helpers::generate_dummy_client_with_spec_and_accounts; use transaction::{Transaction, Action, SignedTransaction}; use tempdir::TempDir; @@ -39,7 +39,7 @@ const TRANSITION_BLOCK_1: usize = 2; // block at which the contract becomes acti const TRANSITION_BLOCK_2: usize = 10; // block at which the second contract activates. macro_rules! secret { - ($e: expr) => { Secret::from_slice(&$crate::hash::keccak($e)) } + ($e: expr) => { Secret::from($crate::hash::keccak($e).0) } } lazy_static! { @@ -52,7 +52,6 @@ lazy_static! { static ref RICH_SECRET: Secret = secret!("1"); } - /// Contract code used here: https://gist.github.com/anonymous/2a43783647e0f0dfcc359bd6fd81d6d9 /// Account with secrets keccak("1") is initially the validator. /// Transitions to the contract at block 2, initially same validator set. @@ -89,7 +88,7 @@ enum Transition { // create a chain with the given transitions and some blocks beyond that transition. fn make_chain(accounts: Arc, blocks_beyond: usize, transitions: Vec) -> Arc { - let client = helpers::generate_dummy_client_with_spec_and_accounts( + let client = generate_dummy_client_with_spec_and_accounts( spec_fixed_to_contract, Some(accounts.clone())); let mut cur_signers = vec![*RICH_ADDR]; @@ -107,13 +106,11 @@ fn make_chain(accounts: Arc, blocks_beyond: usize, transitions: trace!(target: "snapshot", "Pushing block #{}, {} txs, author={}", n, txs.len(), signers[idx]); - client.miner().set_author(signers[idx]); + client.miner().set_author(signers[idx], Some(PASS.into())).unwrap(); client.miner().import_external_transactions(&*client, txs.into_iter().map(Into::into).collect()); - let engine = client.engine(); - engine.set_signer(accounts.clone(), signers[idx], PASS.to_owned()); - engine.step(); + client.engine().step(); assert_eq!(client.chain_info().best_block_number, n); }; @@ -217,7 +214,7 @@ fn fixed_to_contract_only() { secret!("dog42"), ]); - assert!(provider.has_account(*RICH_ADDR).unwrap()); + assert!(provider.has_account(*RICH_ADDR)); let client = make_chain(provider, 3, vec![ Transition::Manual(3, vec![addrs[2], addrs[3], addrs[5], addrs[7]]), @@ -250,7 +247,7 @@ fn fixed_to_contract_to_contract() { secret!("dog42"), ]); - assert!(provider.has_account(*RICH_ADDR).unwrap()); + assert!(provider.has_account(*RICH_ADDR)); let client = make_chain(provider, 3, vec![ Transition::Manual(3, vec![addrs[2], addrs[3], addrs[5], addrs[7]]), diff --git a/ethcore/src/snapshot/tests/proof_of_work.rs b/ethcore/src/snapshot/tests/proof_of_work.rs index e41b61e6e068d9ece97dc6f817aa22c6fae8861c..e689edf80fad9131ad06397147c9d921ebaaaf4d 100644 --- a/ethcore/src/snapshot/tests/proof_of_work.rs +++ b/ethcore/src/snapshot/tests/proof_of_work.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -19,10 +19,10 @@ use std::sync::Arc; use std::sync::atomic::AtomicBool; use tempdir::TempDir; -use error::Error; +use error::{Error, ErrorKind}; use blockchain::generator::{BlockGenerator, BlockBuilder}; -use blockchain::BlockChain; +use blockchain::{BlockChain, ExtrasInsert}; use snapshot::{chunk_secondary, Error as SnapshotError, Progress, SnapshotComponents}; use snapshot::io::{PackedReader, PackedWriter, SnapshotReader, SnapshotWriter}; @@ -49,7 +49,11 @@ fn chunk_and_restore(amount: u64) { // build the blockchain. let mut batch = DBTransaction::new(); for block in generator { - bc.insert_block(&mut batch, &block.encoded(), vec![]); + bc.insert_block(&mut batch, &block.encoded(), vec![], ExtrasInsert { + fork_choice: ::engines::ForkChoice::New, + is_finalized: false, + metadata: None, + }); bc.commit(); } @@ -141,7 +145,7 @@ fn checks_flag() { let mut rebuilder = SNAPSHOT_MODE.rebuilder(chain, db.clone(), &manifest).unwrap(); match rebuilder.feed(&chunk, engine.as_ref(), &AtomicBool::new(false)) { - Err(Error::Snapshot(SnapshotError::RestorationAborted)) => {} + Err(Error(ErrorKind::Snapshot(SnapshotError::RestorationAborted), _)) => {} _ => panic!("Wrong result on abort flag set") } } diff --git a/ethcore/src/snapshot/tests/service.rs b/ethcore/src/snapshot/tests/service.rs index 52b4b3cc979f7c9d77035d46c9a3b59fefb56cb4..55cb0e8338bbd7c592ffde3f387dd0ce9174b15f 100644 --- a/ethcore/src/snapshot/tests/service.rs +++ b/ethcore/src/snapshot/tests/service.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -24,7 +24,8 @@ use ids::BlockId; use snapshot::service::{Service, ServiceParams}; use snapshot::{self, ManifestData, SnapshotService}; use spec::Spec; -use tests::helpers::generate_dummy_client_with_spec_and_data; +use test_helpers::generate_dummy_client_with_spec_and_data; +use test_helpers_internal::restoration_db_handler; use io::IoChannel; use kvdb_rocksdb::{Database, DatabaseConfig}; @@ -58,14 +59,14 @@ fn restored_is_equivalent() { Default::default(), &spec, Arc::new(client_db), - Arc::new(::miner::Miner::with_spec(&spec)), + Arc::new(::miner::Miner::new_for_tests(&spec, None)), IoChannel::disconnected(), ).unwrap(); let service_params = ServiceParams { engine: spec.engine.clone(), genesis_block: spec.genesis_block(), - db_config: db_config, + restoration_db_handler: restoration_db_handler(db_config), pruning: ::journaldb::Algorithm::Archive, channel: IoChannel::disconnected(), snapshot_root: path, @@ -107,7 +108,7 @@ fn guards_delete_folders() { let service_params = ServiceParams { engine: spec.engine.clone(), genesis_block: spec.genesis_block(), - db_config: DatabaseConfig::with_columns(::db::NUM_COLUMNS), + restoration_db_handler: restoration_db_handler(DatabaseConfig::with_columns(::db::NUM_COLUMNS)), pruning: ::journaldb::Algorithm::Archive, channel: IoChannel::disconnected(), snapshot_root: tempdir.path().to_owned(), @@ -129,12 +130,16 @@ fn guards_delete_folders() { service.init_restore(manifest.clone(), true).unwrap(); assert!(path.exists()); + // The `db` folder should have been deleted, + // while the `temp` one kept service.abort_restore(); - assert!(!path.exists()); + assert!(!path.join("db").exists()); + assert!(path.join("temp").exists()); service.init_restore(manifest.clone(), true).unwrap(); assert!(path.exists()); drop(service); - assert!(!path.exists()); + assert!(!path.join("db").exists()); + assert!(path.join("temp").exists()); } diff --git a/ethcore/src/snapshot/tests/state.rs b/ethcore/src/snapshot/tests/state.rs index f61c799933cef9771acb75c6aa8a7e227350a51d..12f19e8c27c1ef565bc15320d5f9d5a63e8239ce 100644 --- a/ethcore/src/snapshot/tests/state.rs +++ b/ethcore/src/snapshot/tests/state.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -26,7 +26,7 @@ use snapshot::{chunk_state, Error as SnapshotError, Progress, StateRebuilder}; use snapshot::io::{PackedReader, PackedWriter, SnapshotReader, SnapshotWriter}; use super::helpers::{compare_dbs, StateProducer}; -use error::Error; +use error::{Error, ErrorKind}; use rand::{XorShiftRng, SeedableRng}; use ethereum_types::H256; @@ -114,7 +114,7 @@ fn get_code_from_prev_chunk() { // first one will have code inlined, // second will just have its hash. let thin_rlp = acc_stream.out(); - let acc: BasicAccount = ::rlp::decode(&thin_rlp); + let acc: BasicAccount = ::rlp::decode(&thin_rlp).expect("error decoding basic account"); let mut make_chunk = |acc, hash| { let mut db = MemoryDB::new(); @@ -189,7 +189,7 @@ fn checks_flag() { let chunk = ::snappy::decompress(&raw).unwrap(); match rebuilder.feed(&chunk, &flag) { - Err(Error::Snapshot(SnapshotError::RestorationAborted)) => {}, + Err(Error(ErrorKind::Snapshot(SnapshotError::RestorationAborted), _)) => {}, _ => panic!("unexpected result when feeding with flag off"), } } diff --git a/ethcore/src/snapshot/traits.rs b/ethcore/src/snapshot/traits.rs index a81c14f08d58937d78b2815d2d7aad620ce99d94..eec629ba6e672134877a4c08d77fccc39432739c 100644 --- a/ethcore/src/snapshot/traits.rs +++ b/ethcore/src/snapshot/traits.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -30,6 +30,9 @@ pub trait SnapshotService : Sync + Send { /// `None` indicates warp sync isn't supported by the consensus engine. fn supported_versions(&self) -> Option<(u64, u64)>; + /// Returns a list of the completed chunks + fn completed_chunks(&self) -> Option>; + /// Get raw chunk for a given hash. fn chunk(&self, hash: H256) -> Option; @@ -51,4 +54,7 @@ pub trait SnapshotService : Sync + Send { /// Feed a raw block chunk to the service to be processed asynchronously. /// no-op if currently restoring. fn restore_block_chunk(&self, hash: H256, chunk: Bytes); + + /// Shutdown the Snapshot Service by aborting any ongoing restore + fn shutdown(&self); } diff --git a/ethcore/src/snapshot/watcher.rs b/ethcore/src/snapshot/watcher.rs index 841fa1982acdc8488994b4936dea6633a816647a..680567962766ffefa4d430f576fb7ddacabf7dc8 100644 --- a/ethcore/src/snapshot/watcher.rs +++ b/ethcore/src/snapshot/watcher.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -17,14 +17,14 @@ //! Watcher for snapshot-related chain events. use parking_lot::Mutex; -use client::{BlockInfo, Client, ChainNotify, ClientIoMessage}; +use client::{BlockInfo, Client, ChainNotify, ChainRoute, ClientIoMessage}; use ids::BlockId; use io::IoChannel; use ethereum_types::H256; use bytes::Bytes; -use std::sync::Arc; +use std::{sync::Arc, time::Duration}; // helper trait for transforming hashes to numbers and checking if syncing. trait Oracle: Send + Sync { @@ -103,11 +103,10 @@ impl ChainNotify for Watcher { &self, imported: Vec, _: Vec, - _: Vec, - _: Vec, + _: ChainRoute, _: Vec, _: Vec, - _duration: u64) + _duration: Duration) { if self.oracle.is_major_importing() { return } @@ -131,11 +130,12 @@ impl ChainNotify for Watcher { mod tests { use super::{Broadcast, Oracle, Watcher}; - use client::ChainNotify; + use client::{ChainNotify, ChainRoute}; use ethereum_types::{H256, U256}; use std::collections::HashMap; + use std::time::Duration; struct TestOracle(HashMap); @@ -158,6 +158,8 @@ mod tests { // helper harness for tests which expect a notification. fn harness(numbers: Vec, period: u64, history: u64, expected: Option) { + const DURATION_ZERO: Duration = Duration::from_millis(0); + let hashes: Vec<_> = numbers.clone().into_iter().map(|x| H256::from(U256::from(x))).collect(); let map = hashes.clone().into_iter().zip(numbers).collect(); @@ -171,11 +173,10 @@ mod tests { watcher.new_blocks( hashes, vec![], + ChainRoute::default(), vec![], vec![], - vec![], - vec![], - 0, + DURATION_ZERO, ); } diff --git a/ethcore/src/spec/genesis.rs b/ethcore/src/spec/genesis.rs index 937d7ed8737460747973003794fa4f5f23de6c66..fbfd2cbc4275d9778f4efc48956e59db961b4d4f 100644 --- a/ethcore/src/spec/genesis.rs +++ b/ethcore/src/spec/genesis.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/src/spec/mod.rs b/ethcore/src/spec/mod.rs index fb60e1cc858efa28746b190b9f025befd95c5131..35705f4a8e5fecffee9623e50cb28e9e71cb3b60 100644 --- a/ethcore/src/spec/mod.rs +++ b/ethcore/src/spec/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/src/spec/seal.rs b/ethcore/src/spec/seal.rs index 2a07e69c43f37b6eb0ff7bef44a32a422bceb9cc..0ed41acc846e8f350613f0ca5e87fc71022fce60 100644 --- a/ethcore/src/spec/seal.rs +++ b/ethcore/src/spec/seal.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/src/spec/spec.rs b/ethcore/src/spec/spec.rs index f0b3b9203bb26a7d41f975aad411af9714c3f5b7..6f785fe7fbae9a27291ac2b345a66b55e65e9d59 100644 --- a/ethcore/src/spec/spec.rs +++ b/ethcore/src/spec/spec.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -48,6 +48,8 @@ use trace::{NoopTracer, NoopVMTracer}; pub use ethash::OptimizeFor; +const MAX_TRANSACTION_SIZE: usize = 300 * 1024; + // helper for formatting errors. fn fmt_err(f: F) -> String { format!("Spec json is invalid: {}", f) @@ -76,6 +78,14 @@ pub struct CommonParams { pub min_gas_limit: U256, /// Fork block to check. pub fork_block: Option<(BlockNumber, H256)>, + /// EIP150 transition block number. + pub eip150_transition: BlockNumber, + /// Number of first block where EIP-160 rules begin. + pub eip160_transition: u64, + /// Number of first block where EIP-161.abc begin. + pub eip161abc_transition: u64, + /// Number of first block where EIP-161.d begins. + pub eip161d_transition: u64, /// Number of first block where EIP-98 rules begin. pub eip98_transition: BlockNumber, /// Number of first block where EIP-658 rules begin. @@ -103,6 +113,8 @@ pub struct CommonParams { pub eip211_transition: BlockNumber, /// Number of first block where EIP-214 rules begin. pub eip214_transition: BlockNumber, + /// Number of first block where EIP-145 rules begin. + pub eip145_transition: BlockNumber, /// Number of first block where dust cleanup rules (EIP-168 and EIP169) begin. pub dust_protection_transition: BlockNumber, /// Nonce cap increase per block. Nonce cap is only checked if dust protection is enabled. @@ -123,14 +135,27 @@ pub struct CommonParams { pub max_code_size_transition: BlockNumber, /// Transaction permission managing contract address. pub transaction_permission_contract: Option
, + /// Maximum size of transaction's RLP payload + pub max_transaction_size: usize, } impl CommonParams { /// Schedule for an EVM in the post-EIP-150-era of the Ethereum main net. pub fn schedule(&self, block_number: u64) -> ::vm::Schedule { - let mut schedule = ::vm::Schedule::new_post_eip150(self.max_code_size(block_number) as _, true, true, true); - self.update_schedule(block_number, &mut schedule); - schedule + if block_number < self.eip150_transition { + ::vm::Schedule::new_homestead() + } else { + let max_code_size = self.max_code_size(block_number); + let mut schedule = ::vm::Schedule::new_post_eip150( + max_code_size as _, + block_number >= self.eip160_transition, + block_number >= self.eip161abc_transition, + block_number >= self.eip161d_transition + ); + + self.update_schedule(block_number, &mut schedule); + schedule + } } /// Returns max code size at given block. @@ -148,6 +173,7 @@ impl CommonParams { schedule.have_revert = block_number >= self.eip140_transition; schedule.have_static_call = block_number >= self.eip214_transition; schedule.have_return_data = block_number >= self.eip211_transition; + schedule.have_bitwise_shifting = block_number >= self.eip145_transition; if block_number >= self.eip210_transition { schedule.blockhash_gas = 800; } @@ -190,20 +216,24 @@ impl From for CommonParams { } else { None }, + eip150_transition: p.eip150_transition.map_or(0, Into::into), + eip160_transition: p.eip160_transition.map_or(0, Into::into), + eip161abc_transition: p.eip161abc_transition.map_or(0, Into::into), + eip161d_transition: p.eip161d_transition.map_or(0, Into::into), eip98_transition: p.eip98_transition.map_or(0, Into::into), eip155_transition: p.eip155_transition.map_or(0, Into::into), validate_receipts_transition: p.validate_receipts_transition.map_or(0, Into::into), validate_chain_id_transition: p.validate_chain_id_transition.map_or(0, Into::into), - eip86_transition: p.eip86_transition.map_or( - BlockNumber::max_value(), + eip86_transition: p.eip86_transition.map_or_else( + BlockNumber::max_value, Into::into, ), - eip140_transition: p.eip140_transition.map_or( - BlockNumber::max_value(), + eip140_transition: p.eip140_transition.map_or_else( + BlockNumber::max_value, Into::into, ), - eip210_transition: p.eip210_transition.map_or( - BlockNumber::max_value(), + eip210_transition: p.eip210_transition.map_or_else( + BlockNumber::max_value, Into::into, ), eip210_contract_address: p.eip210_contract_address.map_or(0xf0.into(), Into::into), @@ -216,20 +246,24 @@ impl From for CommonParams { Into::into, ), eip210_contract_gas: p.eip210_contract_gas.map_or(1000000.into(), Into::into), - eip211_transition: p.eip211_transition.map_or( - BlockNumber::max_value(), + eip211_transition: p.eip211_transition.map_or_else( + BlockNumber::max_value, + Into::into, + ), + eip145_transition: p.eip145_transition.map_or_else( + BlockNumber::max_value, Into::into, ), - eip214_transition: p.eip214_transition.map_or( - BlockNumber::max_value(), + eip214_transition: p.eip214_transition.map_or_else( + BlockNumber::max_value, Into::into, ), - eip658_transition: p.eip658_transition.map_or( - BlockNumber::max_value(), + eip658_transition: p.eip658_transition.map_or_else( + BlockNumber::max_value, Into::into, ), - dust_protection_transition: p.dust_protection_transition.map_or( - BlockNumber::max_value(), + dust_protection_transition: p.dust_protection_transition.map_or_else( + BlockNumber::max_value, Into::into, ), nonce_cap_increment: p.nonce_cap_increment.map_or(64, Into::into), @@ -238,10 +272,11 @@ impl From for CommonParams { registrar: p.registrar.map_or_else(Address::new, Into::into), node_permission_contract: p.node_permission_contract.map(Into::into), max_code_size: p.max_code_size.map_or(u64::max_value(), Into::into), + max_transaction_size: p.max_transaction_size.map_or(MAX_TRANSACTION_SIZE, Into::into), max_code_size_transition: p.max_code_size_transition.map_or(0, Into::into), transaction_permission_contract: p.transaction_permission_contract.map(Into::into), - wasm_activation_transition: p.wasm_activation_transition.map_or( - BlockNumber::max_value(), + wasm_activation_transition: p.wasm_activation_transition.map_or_else( + BlockNumber::max_value, Into::into ), } @@ -285,7 +320,6 @@ impl<'a, T: AsRef> From<&'a T> for SpecParams<'a> { } } - /// Parameters for a block chain; includes both those intrinsic to the design of the /// chain and those to be interpreted by the active chain engine. pub struct Spec { @@ -481,6 +515,7 @@ macro_rules! load_bundled { }; } +#[cfg(any(test, feature = "test-helpers"))] macro_rules! load_machine_bundled { ($e:expr) => { Spec::load_machine( @@ -771,7 +806,7 @@ impl Spec { author: *genesis.author(), timestamp: genesis.timestamp(), difficulty: *genesis.difficulty(), - gas_limit: *genesis.gas_limit(), + gas_limit: U256::max_value(), last_hashes: Arc::new(Vec::new()), gas_used: 0.into(), }; @@ -780,7 +815,7 @@ impl Spec { let tx = Transaction { nonce: self.engine.account_start_nonce(0), action: Action::Call(a), - gas: U256::from(50_000_000), // TODO: share with client. + gas: U256::max_value(), gas_price: U256::default(), value: U256::default(), data: d, @@ -804,39 +839,44 @@ impl Spec { self.engine.genesis_epoch_data(&genesis, &call) } + /// Create a new Spec with InstantSeal consensus which does internal sealing (not requiring + /// work). + pub fn new_instant() -> Spec { + load_bundled!("instant_seal") + } + /// Create a new Spec which conforms to the Frontier-era Morden chain except that it's a /// NullEngine consensus. + #[cfg(any(test, feature = "test-helpers"))] pub fn new_test() -> Spec { load_bundled!("null_morden") } /// Create the EthereumMachine corresponding to Spec::new_test. + #[cfg(any(test, feature = "test-helpers"))] pub fn new_test_machine() -> EthereumMachine { load_machine_bundled!("null_morden") } - /// Create a new Spec which conforms to the Frontier-era Morden chain except that it's a NullEngine consensus with applying reward on block close. + #[cfg(any(test, feature = "test-helpers"))] pub fn new_test_with_reward() -> Spec { load_bundled!("null_morden_with_reward") } /// Create a new Spec which is a NullEngine consensus with a premine of address whose /// secret is keccak(''). + #[cfg(any(test, feature = "test-helpers"))] pub fn new_null() -> Spec { load_bundled!("null") } /// Create a new Spec which constructs a contract at address 5 with storage at 0 equal to 1. + #[cfg(any(test, feature = "test-helpers"))] pub fn new_test_constructor() -> Spec { load_bundled!("constructor") } - /// Create a new Spec with InstantSeal consensus which does internal sealing (not requiring - /// work). - pub fn new_instant() -> Spec { - load_bundled!("instant_seal") - } - /// Create a new Spec with AuthorityRound consensus which does internal sealing (not /// requiring work). /// Accounts with secrets keccak("0") and keccak("1") are the validators. + #[cfg(any(test, feature = "test-helpers"))] pub fn new_test_round() -> Self { load_bundled!("authority_round") } @@ -844,13 +884,23 @@ impl Spec { /// Create a new Spec with AuthorityRound consensus which does internal sealing (not /// requiring work) with empty step messages enabled. /// Accounts with secrets keccak("0") and keccak("1") are the validators. + #[cfg(any(test, feature = "test-helpers"))] pub fn new_test_round_empty_steps() -> Self { load_bundled!("authority_round_empty_steps") } + /// Create a new Spec with AuthorityRound consensus (with empty steps) using a block reward + /// contract. The contract source code can be found at: + /// https://github.com/parity-contracts/block-reward/blob/daf7d44383b6cdb11cb6b953b018648e2b027cfb/contracts/ExampleBlockReward.sol + #[cfg(any(test, feature = "test-helpers"))] + pub fn new_test_round_block_reward_contract() -> Self { + load_bundled!("authority_round_block_reward_contract") + } + /// Create a new Spec with Tendermint consensus which does internal sealing (not requiring /// work). /// Account keccak("0") and keccak("1") are a authorities. + #[cfg(any(test, feature = "test-helpers"))] pub fn new_test_tendermint() -> Self { load_bundled!("tendermint") } @@ -863,6 +913,7 @@ impl Spec { /// "0xbfc708a000000000000000000000000082a978b3f5962a5b0957d9ee9eef472ee55b42f1" and added /// back in using /// "0x4d238c8e00000000000000000000000082a978b3f5962a5b0957d9ee9eef472ee55b42f1". + #[cfg(any(test, feature = "test-helpers"))] pub fn new_validator_safe_contract() -> Self { load_bundled!("validator_safe_contract") } @@ -870,6 +921,7 @@ impl Spec { /// The same as the `safeContract`, but allows reporting and uses AuthorityRound. /// Account is marked with `reportBenign` it can be checked as disliked with "0xd8f2e0bf". /// Validator can be removed with `reportMalicious`. + #[cfg(any(test, feature = "test-helpers"))] pub fn new_validator_contract() -> Self { load_bundled!("validator_contract") } @@ -878,21 +930,17 @@ impl Spec { /// height. /// Account with secrets keccak("0") is the validator for block 1 and with keccak("1") /// onwards. + #[cfg(any(test, feature = "test-helpers"))] pub fn new_validator_multi() -> Self { load_bundled!("validator_multi") } - - /// Create a new spec for a PoW chain - pub fn new_pow_test_spec() -> Self { - load_bundled!("ethereum/olympic") - } } #[cfg(test)] mod tests { use super::*; use state::State; - use tests::helpers::get_temp_state_db; + use test_helpers::get_temp_state_db; use views::BlockView; use tempdir::TempDir; @@ -913,7 +961,7 @@ mod tests { ); let genesis = test_spec.genesis_block(); assert_eq!( - BlockView::new(&genesis).header_view().hash(), + view!(BlockView, &genesis).header_view().hash(), "0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303".into() ); } diff --git a/ethcore/src/state/account.rs b/ethcore/src/state/account.rs old mode 100755 new mode 100644 index 5ed9d9a11475893cdf829140cb36d53391635d2a..5e99e7fbeb21dcde7cffcaa59fd45df2b0c4d626 --- a/ethcore/src/state/account.rs +++ b/ethcore/src/state/account.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -21,13 +21,14 @@ use std::sync::Arc; use std::collections::{HashMap, BTreeMap}; use hash::{KECCAK_EMPTY, KECCAK_NULL_RLP, keccak}; use ethereum_types::{H256, U256, Address}; +use error::Error; use hashdb::HashDB; use kvdb::DBValue; use bytes::{Bytes, ToPretty}; use trie; use trie::{SecTrieDB, Trie, TrieFactory, TrieError}; use pod_account::*; -use rlp::*; +use rlp::{RlpStream, encode}; use lru_cache::LruCache; use basic_account::BasicAccount; @@ -144,9 +145,10 @@ impl Account { } /// Create a new account from RLP. - pub fn from_rlp(rlp: &[u8]) -> Account { - let basic: BasicAccount = ::rlp::decode(rlp); - basic.into() + pub fn from_rlp(rlp: &[u8]) -> Result { + ::rlp::decode::(rlp) + .map(|ba| ba.into()) + .map_err(|e| e.into()) } /// Create a new contract account. @@ -180,6 +182,16 @@ impl Account { self.init_code(code); } + /// Reset this account's code and storage to given values. + pub fn reset_code_and_storage(&mut self, code: Arc, storage: HashMap) { + self.code_hash = keccak(&*code); + self.code_cache = code; + self.code_size = Some(self.code_cache.len()); + self.code_filth = Filth::Dirty; + self.storage_cache = Self::empty_storage_cache(); + self.storage_changes = storage; + } + /// Set (and cache) the contents of the trie's storage at `key` to `value`. pub fn set_storage(&mut self, key: H256, value: H256) { self.storage_changes.insert(key, value); @@ -192,8 +204,8 @@ impl Account { return Ok(value); } let db = SecTrieDB::new(db, &self.storage_root)?; - - let item: U256 = db.get_with(key, ::rlp::decode)?.unwrap_or_else(U256::zero); + let panicky_decoder = |bytes:&[u8]| ::rlp::decode(&bytes).expect("decoding db value failed"); + let item: U256 = db.get_with(key, panicky_decoder)?.unwrap_or_else(U256::zero); let value: H256 = item.into(); self.storage_cache.borrow_mut().insert(key.clone(), value.clone()); Ok(value) @@ -424,7 +436,6 @@ impl Account { pub fn clone_dirty(&self) -> Account { let mut account = self.clone_basic(); account.storage_changes = self.storage_changes.clone(); - account.code_cache = self.code_cache.clone(); account } @@ -449,7 +460,7 @@ impl Account { self.address_hash = other.address_hash; let mut cache = self.storage_cache.borrow_mut(); for (k, v) in other.storage_cache.into_inner() { - cache.insert(k.clone() , v.clone()); //TODO: cloning should not be required here + cache.insert(k, v); } self.storage_changes = other.storage_changes; } @@ -469,7 +480,8 @@ impl Account { let trie = TrieDB::new(db, &self.storage_root)?; let item: U256 = { - let query = (&mut recorder, ::rlp::decode); + let panicky_decoder = |bytes:&[u8]| ::rlp::decode(bytes).expect("decoding db value failed"); + let query = (&mut recorder, panicky_decoder); trie.get_with(&storage_key, query)?.unwrap_or_else(U256::zero) }; @@ -519,7 +531,7 @@ mod tests { a.rlp() }; - let a = Account::from_rlp(&rlp); + let a = Account::from_rlp(&rlp).expect("decoding db value failed"); assert_eq!(*a.storage_root().unwrap(), "c57e1afb758b07f8d2c8f13a3b6e44fa5ff94ab266facc5a4fd3f062426e50b2".into()); assert_eq!(a.storage_at(&db.immutable(), &0x00u64.into()).unwrap(), 0x1234u64.into()); assert_eq!(a.storage_at(&db.immutable(), &0x01u64.into()).unwrap(), H256::default()); @@ -537,10 +549,10 @@ mod tests { a.rlp() }; - let mut a = Account::from_rlp(&rlp); + let mut a = Account::from_rlp(&rlp).expect("decoding db value failed"); assert!(a.cache_code(&db.immutable()).is_some()); - let mut a = Account::from_rlp(&rlp); + let mut a = Account::from_rlp(&rlp).expect("decoding db value failed"); assert_eq!(a.note_code(vec![0x55, 0x44, 0xffu8]), Ok(())); } @@ -600,7 +612,7 @@ mod tests { #[test] fn rlpio() { let a = Account::new(69u8.into(), 0u8.into(), HashMap::new(), Bytes::new()); - let b = Account::from_rlp(&a.rlp()); + let b = Account::from_rlp(&a.rlp()).unwrap(); assert_eq!(a.balance(), b.balance()); assert_eq!(a.nonce(), b.nonce()); assert_eq!(a.code_hash(), b.code_hash()); diff --git a/ethcore/src/state/backend.rs b/ethcore/src/state/backend.rs index 1e761506d95703afaac0df33f2468578f775b9ed..6b2e21cb4654225c34a5508e4e55a5e94e6e75d0 100644 --- a/ethcore/src/state/backend.rs +++ b/ethcore/src/state/backend.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/src/state/mod.rs b/ethcore/src/state/mod.rs old mode 100755 new mode 100644 index 1fe4dd4d6d990ea12dd7abe3461e195001cc3971..ccca20b71e18b568ee71a374353a18134dd9e9f3 --- a/ethcore/src/state/mod.rs +++ b/ethcore/src/state/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -21,7 +21,7 @@ use std::cell::{RefCell, RefMut}; use std::collections::hash_map::Entry; -use std::collections::{HashMap, BTreeMap, HashSet}; +use std::collections::{HashMap, BTreeMap, BTreeSet, HashSet}; use std::fmt; use std::sync::Arc; use hash::{KECCAK_NULL_RLP, KECCAK_EMPTY}; @@ -51,7 +51,6 @@ use trie; use trie::{Trie, TrieError, TrieDB}; use trie::recorder::Recorder; - mod account; mod substate; @@ -402,19 +401,6 @@ impl State { self.factories.vm.clone() } - /// Swap the current backend for another. - // TODO: [rob] find a less hacky way to avoid duplication of `Client::state_at`. - pub fn replace_backend(self, backend: T) -> State { - State { - db: backend, - root: self.root, - cache: self.cache, - checkpoints: self.checkpoints, - account_start_nonce: self.account_start_nonce, - factories: self.factories, - } - } - /// Create a recoverable checkpoint of this state. pub fn checkpoint(&mut self) { self.checkpoints.get_mut().push(HashMap::new()); @@ -494,6 +480,13 @@ impl State { (self.root, self.db) } + /// Destroy the current object and return single account data. + pub fn into_account(self, account: &Address) -> trie::Result<(Option>, HashMap)> { + // TODO: deconstruct without cloning. + let account = self.require(account, true)?; + Ok((account.code().clone(), account.storage_changes().clone())) + } + /// Return reference to root pub fn root(&self) -> &H256 { &self.root @@ -598,7 +591,8 @@ impl State { // account is not found in the global cache, get from the DB and insert into local let db = self.factories.trie.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR); - let maybe_acc = db.get_with(address, Account::from_rlp)?; + let from_rlp = |b: &[u8]| Account::from_rlp(b).expect("decoding db value failed"); + let maybe_acc = db.get_with(address, from_rlp)?; let r = maybe_acc.as_ref().map_or(Ok(H256::new()), |a| { let account_db = self.factories.accountdb.readonly(self.db.as_hashdb(), a.address_hash(address)); a.storage_at(account_db.as_hashdb(), key) @@ -855,29 +849,65 @@ impl State { })) } - fn query_pod(&mut self, query: &PodState) -> trie::Result<()> { - for (address, pod_account) in query.get() { - if !self.ensure_cached(address, RequireCache::Code, true, |a| a.is_some())? { - continue - } + /// Populate a PodAccount map from this state, with another state as the account and storage query. + pub fn to_pod_diff(&mut self, query: &State) -> trie::Result { + assert!(self.checkpoints.borrow().is_empty()); + + // Merge PodAccount::to_pod for cache of self and `query`. + let all_addresses = self.cache.borrow().keys().cloned() + .chain(query.cache.borrow().keys().cloned()) + .collect::>(); + + Ok(PodState::from(all_addresses.into_iter().fold(Ok(BTreeMap::new()), |m: trie::Result<_>, address| { + let mut m = m?; + + let account = self.ensure_cached(&address, RequireCache::Code, true, |acc| { + acc.map(|acc| { + // Merge all modified storage keys. + let all_keys = { + let self_keys = acc.storage_changes().keys().cloned() + .collect::>(); + + if let Some(ref query_storage) = query.cache.borrow().get(&address) + .and_then(|opt| { + Some(opt.account.as_ref()?.storage_changes().keys().cloned() + .collect::>()) + }) + { + self_keys.union(&query_storage).cloned().collect::>() + } else { + self_keys.into_iter().collect::>() + } + }; - // needs to be split into two parts for the refcell code here - // to work. - for key in pod_account.storage.keys() { - self.storage_at(address, key)?; + // Storage must be fetched after ensure_cached to avoid borrow problem. + (*acc.balance(), *acc.nonce(), all_keys, acc.code().map(|x| x.to_vec())) + }) + })?; + + if let Some((balance, nonce, storage_keys, code)) = account { + let storage = storage_keys.into_iter().fold(Ok(BTreeMap::new()), |s: trie::Result<_>, key| { + let mut s = s?; + + s.insert(key, self.storage_at(&address, &key)?); + Ok(s) + })?; + + m.insert(address, PodAccount { + balance, nonce, storage, code + }); } - } - Ok(()) + Ok(m) + })?)) } /// Returns a `StateDiff` describing the difference from `orig` to `self`. /// Consumes self. - pub fn diff_from(&self, orig: State) -> trie::Result { + pub fn diff_from(&self, mut orig: State) -> trie::Result { let pod_state_post = self.to_pod(); - let mut state_pre = orig; - state_pre.query_pod(&pod_state_post)?; - Ok(pod_state::diff_pod(&state_pre.to_pod(), &pod_state_post)) + let pod_state_pre = orig.to_pod_diff(self)?; + Ok(pod_state::diff_pod(&pod_state_pre, &pod_state_post)) } // load required account data from the databases. @@ -940,7 +970,8 @@ impl State { // not found in the global cache, get from the DB and insert into local let db = self.factories.trie.readonly(self.db.as_hashdb(), &self.root)?; - let mut maybe_acc = db.get_with(a, Account::from_rlp)?; + let from_rlp = |b: &[u8]| Account::from_rlp(b).expect("decoding db value failed"); + let mut maybe_acc = db.get_with(a, from_rlp)?; if let Some(ref mut account) = maybe_acc.as_mut() { let accountdb = self.factories.accountdb.readonly(self.db.as_hashdb(), account.address_hash(a)); Self::update_account_cache(require, account, &self.db, accountdb.as_hashdb()); @@ -969,7 +1000,8 @@ impl State { None => { let maybe_acc = if !self.db.is_known_null(a) { let db = self.factories.trie.readonly(self.db.as_hashdb(), &self.root)?; - AccountEntry::new_clean(db.get_with(a, Account::from_rlp)?) + let from_rlp = |b:&[u8]| { Account::from_rlp(b).expect("decoding db value failed") }; + AccountEntry::new_clean(db.get_with(a, from_rlp)?) } else { AccountEntry::new_clean(None) }; @@ -1003,6 +1035,11 @@ impl State { } })) } + + /// Replace account code and storage. Creates account if it does not exist. + pub fn patch_account(&self, a: &Address, code: Arc, storage: HashMap) -> trie::Result<()> { + Ok(self.require(a, false)?.reset_code_and_storage(code, storage)) + } } // State proof implementations; useful for light client protocols. @@ -1016,7 +1053,10 @@ impl State { let mut recorder = Recorder::new(); let trie = TrieDB::new(self.db.as_hashdb(), &self.root)?; let maybe_account: Option = { - let query = (&mut recorder, ::rlp::decode); + let panicky_decoder = |bytes: &[u8]| { + ::rlp::decode(bytes).expect(&format!("prove_account, could not query trie for account key={}", &account_key)) + }; + let query = (&mut recorder, panicky_decoder); trie.get_with(&account_key, query)? }; let account = maybe_account.unwrap_or_else(|| BasicAccount { @@ -1038,7 +1078,8 @@ impl State { // TODO: probably could look into cache somehow but it's keyed by // address, not keccak(address). let trie = TrieDB::new(self.db.as_hashdb(), &self.root)?; - let acc = match trie.get_with(&account_key, Account::from_rlp)? { + let from_rlp = |b: &[u8]| Account::from_rlp(b).expect("decoding db value failed"); + let acc = match trie.get_with(&account_key, from_rlp)? { Some(acc) => acc, None => return Ok((Vec::new(), H256::new())), }; @@ -1088,7 +1129,7 @@ mod tests { use super::*; use ethkey::Secret; use ethereum_types::{H256, U256, Address}; - use tests::helpers::{get_temp_state, get_temp_state_db}; + use test_helpers::{get_temp_state, get_temp_state_db}; use machine::EthereumMachine; use vm::EnvInfo; use spec::*; @@ -2202,4 +2243,72 @@ mod tests { assert!(state.exists(&d).unwrap()); assert!(!state.exists(&e).unwrap()); } + + #[test] + fn should_trace_diff_suicided_accounts() { + use pod_account; + + let a = 10.into(); + let db = get_temp_state_db(); + let (root, db) = { + let mut state = State::new(db, U256::from(0), Default::default()); + state.add_balance(&a, &100.into(), CleanupMode::ForceCreate).unwrap(); + state.commit().unwrap(); + state.drop() + }; + + let mut state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); + let original = state.clone(); + state.kill_account(&a); + + let diff = state.diff_from(original).unwrap(); + let diff_map = diff.get(); + assert_eq!(diff_map.len(), 1); + assert!(diff_map.get(&a).is_some()); + assert_eq!(diff_map.get(&a), + pod_account::diff_pod(Some(&PodAccount { + balance: U256::from(100), + nonce: U256::zero(), + code: Some(Default::default()), + storage: Default::default() + }), None).as_ref()); + } + + #[test] + fn should_trace_diff_unmodified_storage() { + use pod_account; + + let a = 10.into(); + let db = get_temp_state_db(); + + let (root, db) = { + let mut state = State::new(db, U256::from(0), Default::default()); + state.set_storage(&a, H256::from(&U256::from(1u64)), H256::from(&U256::from(20u64))).unwrap(); + state.commit().unwrap(); + state.drop() + }; + + let mut state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); + let original = state.clone(); + state.set_storage(&a, H256::from(&U256::from(1u64)), H256::from(&U256::from(100u64))).unwrap(); + + let diff = state.diff_from(original).unwrap(); + let diff_map = diff.get(); + assert_eq!(diff_map.len(), 1); + assert!(diff_map.get(&a).is_some()); + assert_eq!(diff_map.get(&a), + pod_account::diff_pod(Some(&PodAccount { + balance: U256::zero(), + nonce: U256::zero(), + code: Some(Default::default()), + storage: vec![(H256::from(&U256::from(1u64)), H256::from(&U256::from(20u64)))] + .into_iter().collect(), + }), Some(&PodAccount { + balance: U256::zero(), + nonce: U256::zero(), + code: Some(Default::default()), + storage: vec![(H256::from(&U256::from(1u64)), H256::from(&U256::from(100u64)))] + .into_iter().collect(), + })).as_ref()); + } } diff --git a/ethcore/src/state/substate.rs b/ethcore/src/state/substate.rs index e70178a36255133565b01c84a031850ebed66a44..c2f3c62dcb53c87dc5a8dd86d1137b5e8daf0250 100644 --- a/ethcore/src/state/substate.rs +++ b/ethcore/src/state/substate.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/src/state_db.rs b/ethcore/src/state_db.rs old mode 100755 new mode 100644 index 346ad9cc2a69f9a282c431e0d6cd30d9504c4360..c3704828c256bd1de6663f43c134667c7180007a --- a/ethcore/src/state_db.rs +++ b/ethcore/src/state_db.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -480,7 +480,7 @@ unsafe impl Sync for SyncAccount {} mod tests { use ethereum_types::{H256, U256, Address}; use kvdb::DBTransaction; - use tests::helpers::{get_temp_state_db}; + use test_helpers::get_temp_state_db; use state::{Account, Backend}; use ethcore_logger::init_log; diff --git a/ethcore/src/tests/helpers.rs b/ethcore/src/test_helpers.rs similarity index 77% rename from ethcore/src/tests/helpers.rs rename to ethcore/src/test_helpers.rs index fd37164dcbbd71b3d88300187db6011e4f3d35dc..4a83752c04619a361aacca5cdb09c192949de1ae 100644 --- a/ethcore/src/tests/helpers.rs +++ b/ethcore/src/test_helpers.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -14,12 +14,14 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +//! Set of different helpers for client tests + use account_provider::AccountProvider; -use ethereum_types::{H256, U256}; +use ethereum_types::{H256, U256, Address}; use block::{OpenBlock, Drain}; -use blockchain::{BlockChain, Config as BlockChainConfig}; +use blockchain::{BlockChain, Config as BlockChainConfig, ExtrasInsert}; use bytes::Bytes; -use client::{Client, ClientConfig, ChainInfo, ImportBlock, ChainNotify}; +use client::{Client, ClientConfig, ChainInfo, ImportBlock, ChainNotify, ChainMessageType, PrepareOpenBlock}; use ethkey::KeyPair; use evm::Factory as EvmFactory; use factory::Factories; @@ -36,6 +38,7 @@ use std::sync::Arc; use transaction::{Action, Transaction, SignedTransaction}; use views::BlockView; +/// Creates test block with corresponding header pub fn create_test_block(header: &Header) -> Bytes { let mut rlp = RlpStream::new_list(3); rlp.append(header); @@ -73,6 +76,7 @@ fn create_unverifiable_block(order: u32, parent_hash: H256) -> Bytes { create_test_block(&create_unverifiable_block_header(order, parent_hash)) } +/// Creates test block with corresponding header and data pub fn create_test_block_with_data(header: &Header, transactions: &[SignedTransaction], uncles: &[Header]) -> Bytes { let mut rlp = RlpStream::new_list(3); rlp.append(header); @@ -84,23 +88,27 @@ pub fn create_test_block_with_data(header: &Header, transactions: &[SignedTransa rlp.out() } +/// Generates dummy client (not test client) with corresponding amount of blocks pub fn generate_dummy_client(block_number: u32) -> Arc { generate_dummy_client_with_spec_and_data(Spec::new_test, block_number, 0, &[]) } +/// Generates dummy client (not test client) with corresponding amount of blocks and txs per every block pub fn generate_dummy_client_with_data(block_number: u32, txs_per_block: usize, tx_gas_prices: &[U256]) -> Arc { generate_dummy_client_with_spec_and_data(Spec::new_null, block_number, txs_per_block, tx_gas_prices) } - +/// Generates dummy client (not test client) with corresponding amount of blocks, txs per block and spec pub fn generate_dummy_client_with_spec_and_data(test_spec: F, block_number: u32, txs_per_block: usize, tx_gas_prices: &[U256]) -> Arc where F: Fn()->Spec { generate_dummy_client_with_spec_accounts_and_data(test_spec, None, block_number, txs_per_block, tx_gas_prices) } +/// Generates dummy client (not test client) with corresponding spec and accounts pub fn generate_dummy_client_with_spec_and_accounts(test_spec: F, accounts: Option>) -> Arc where F: Fn()->Spec { generate_dummy_client_with_spec_accounts_and_data(test_spec, accounts, 0, 0, &[]) } +/// Generates dummy client (not test client) with corresponding blocks, accounts and spec pub fn generate_dummy_client_with_spec_accounts_and_data(test_spec: F, accounts: Option>, block_number: u32, txs_per_block: usize, tx_gas_prices: &[U256]) -> Arc where F: Fn()->Spec { let test_spec = test_spec(); let client_db = new_db(); @@ -109,7 +117,7 @@ pub fn generate_dummy_client_with_spec_accounts_and_data(test_spec: F, accoun ClientConfig::default(), &test_spec, client_db, - Arc::new(Miner::with_spec_and_accounts(&test_spec, accounts)), + Arc::new(Miner::new_for_tests(&test_spec, accounts)), IoChannel::disconnected(), ).unwrap(); let test_engine = &*test_spec.engine; @@ -140,6 +148,7 @@ pub fn generate_dummy_client_with_spec_accounts_and_data(test_spec: F, accoun (3141562.into(), 31415620.into()), vec![], false, + &mut Vec::new().into_iter(), ).unwrap(); rolling_timestamp += 10; b.set_timestamp(rolling_timestamp); @@ -163,7 +172,7 @@ pub fn generate_dummy_client_with_spec_accounts_and_data(test_spec: F, accoun panic!("error importing block which is valid by definition: {:?}", e); } - last_header = BlockView::new(&b.rlp_bytes()).header(); + last_header = view!(BlockView, &b.rlp_bytes()).header(); db = b.drain(); } client.flush_queue(); @@ -171,6 +180,7 @@ pub fn generate_dummy_client_with_spec_accounts_and_data(test_spec: F, accoun client } +/// Adds blocks to the client pub fn push_blocks_to_client(client: &Arc, timestamp_salt: u64, starting_number: usize, block_number: usize) { let test_spec = Spec::new_test(); let state_root = test_spec.genesis_header().state_root().clone(); @@ -200,6 +210,29 @@ pub fn push_blocks_to_client(client: &Arc, timestamp_salt: u64, starting } } +/// Adds one block with transactions +pub fn push_block_with_transactions(client: &Arc, transactions: &[SignedTransaction]) { + let test_spec = Spec::new_test(); + let test_engine = &*test_spec.engine; + let block_number = client.chain_info().best_block_number as u64 + 1; + + let mut b = client.prepare_open_block(Address::default(), (0.into(), 5000000.into()), Bytes::new()); + b.set_timestamp(block_number * 10); + + for t in transactions { + b.push_transaction(t.clone(), None).unwrap(); + } + let b = b.close_and_lock().seal(test_engine, vec![]).unwrap(); + + if let Err(e) = client.import_block(b.rlp_bytes()) { + panic!("error importing block which is valid by definition: {:?}", e); + } + + client.flush_queue(); + client.import_verified_blocks(); +} + +/// Creates dummy client (not test client) with corresponding blocks pub fn get_test_client_with_blocks(blocks: Vec) -> Arc { let test_spec = Spec::new_test(); let client_db = new_db(); @@ -208,7 +241,7 @@ pub fn get_test_client_with_blocks(blocks: Vec) -> Arc { ClientConfig::default(), &test_spec, client_db, - Arc::new(Miner::with_spec(&test_spec)), + Arc::new(Miner::new_for_tests(&test_spec, None)), IoChannel::disconnected(), ).unwrap(); @@ -226,44 +259,58 @@ fn new_db() -> Arc<::kvdb::KeyValueDB> { Arc::new(::kvdb_memorydb::create(::db::NUM_COLUMNS.unwrap_or(0))) } +/// Generates dummy blockchain with corresponding amount of blocks pub fn generate_dummy_blockchain(block_number: u32) -> BlockChain { let db = new_db(); let bc = BlockChain::new(BlockChainConfig::default(), &create_unverifiable_block(0, H256::zero()), db.clone()); let mut batch = db.transaction(); for block_order in 1..block_number { - bc.insert_block(&mut batch, &create_unverifiable_block(block_order, bc.best_block_hash()), vec![]); + // Total difficulty is always 0 here. + bc.insert_block(&mut batch, &create_unverifiable_block(block_order, bc.best_block_hash()), vec![], ExtrasInsert { + fork_choice: ::engines::ForkChoice::New, + is_finalized: false, + metadata: None, + }); bc.commit(); } db.write(batch).unwrap(); bc } +/// Generates dummy blockchain with corresponding amount of blocks (using creation with extra method for blocks creation) pub fn generate_dummy_blockchain_with_extra(block_number: u32) -> BlockChain { let db = new_db(); let bc = BlockChain::new(BlockChainConfig::default(), &create_unverifiable_block(0, H256::zero()), db.clone()); - let mut batch = db.transaction(); for block_order in 1..block_number { - bc.insert_block(&mut batch, &create_unverifiable_block_with_extra(block_order, bc.best_block_hash(), None), vec![]); + // Total difficulty is always 0 here. + bc.insert_block(&mut batch, &create_unverifiable_block_with_extra(block_order, bc.best_block_hash(), None), vec![], ExtrasInsert { + fork_choice: ::engines::ForkChoice::New, + is_finalized: false, + metadata: None, + }); bc.commit(); } db.write(batch).unwrap(); bc } +/// Returns empty dummy blockchain pub fn generate_dummy_empty_blockchain() -> BlockChain { let db = new_db(); let bc = BlockChain::new(BlockChainConfig::default(), &create_unverifiable_block(0, H256::zero()), db.clone()); bc } +/// Returns temp state pub fn get_temp_state() -> State<::state_db::StateDB> { let journal_db = get_temp_state_db(); State::new(journal_db, U256::from(0), Default::default()) } +/// Returns temp state using coresponding factory pub fn get_temp_state_with_factory(factory: EvmFactory) -> State<::state_db::StateDB> { let journal_db = get_temp_state_db(); let mut factories = Factories::default(); @@ -271,17 +318,20 @@ pub fn get_temp_state_with_factory(factory: EvmFactory) -> State<::state_db::Sta State::new(journal_db, U256::from(0), factories) } +/// Returns temp state db pub fn get_temp_state_db() -> StateDB { let db = new_db(); let journal_db = ::journaldb::new(db, ::journaldb::Algorithm::EarlyMerge, ::db::COL_STATE); StateDB::new(journal_db, 5 * 1024 * 1024) } +/// Returns sequence of hashes of the dummy blocks pub fn get_good_dummy_block_seq(count: usize) -> Vec { let test_spec = Spec::new_test(); get_good_dummy_block_fork_seq(1, count, &test_spec.genesis_header().hash()) } +/// Returns sequence of hashes of the dummy blocks beginning from corresponding parent pub fn get_good_dummy_block_fork_seq(start_number: usize, count: usize, parent_hash: &H256) -> Vec { let test_spec = Spec::new_test(); let genesis_gas = test_spec.genesis_header().gas_limit().clone(); @@ -305,6 +355,7 @@ pub fn get_good_dummy_block_fork_seq(start_number: usize, count: usize, parent_h r } +/// Returns hash and header of the correct dummy block pub fn get_good_dummy_block_hash() -> (H256, Bytes) { let mut block_header = Header::new(); let test_spec = Spec::new_test(); @@ -319,11 +370,13 @@ pub fn get_good_dummy_block_hash() -> (H256, Bytes) { (block_header.hash(), create_test_block(&block_header)) } +/// Returns hash of the correct dummy block pub fn get_good_dummy_block() -> Bytes { let (_, bytes) = get_good_dummy_block_hash(); bytes } +/// Returns hash of the dummy block with incorrect state root pub fn get_bad_state_dummy_block() -> Bytes { let mut block_header = Header::new(); let test_spec = Spec::new_test(); @@ -339,13 +392,20 @@ pub fn get_bad_state_dummy_block() -> Bytes { create_test_block(&block_header) } +/// Test actor for chain events #[derive(Default)] pub struct TestNotify { + /// Messages store pub messages: RwLock>, } impl ChainNotify for TestNotify { - fn broadcast(&self, data: Vec) { + fn broadcast(&self, message: ChainMessageType) { + let data = match message { + ChainMessageType::Consensus(data) => data, + ChainMessageType::SignedPrivateTransaction(data) => data, + ChainMessageType::PrivateTransaction(data) => data, + }; self.messages.write().push(data); } } diff --git a/ethcore/src/test_helpers_internal.rs b/ethcore/src/test_helpers_internal.rs new file mode 100644 index 0000000000000000000000000000000000000000..7319d2d76374cd395df9682fa40e92e66b368a0c --- /dev/null +++ b/ethcore/src/test_helpers_internal.rs @@ -0,0 +1,39 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Internal helpers for client tests + +use std::path::Path; +use std::sync::Arc; +use kvdb::{KeyValueDB, KeyValueDBHandler}; +use kvdb_rocksdb::{Database, DatabaseConfig}; + +/// Creates new instance of KeyValueDBHandler +pub fn restoration_db_handler(config: DatabaseConfig) -> Box { + use kvdb::Error; + + struct RestorationDBHandler { + config: DatabaseConfig, + } + + impl KeyValueDBHandler for RestorationDBHandler { + fn open(&self, db_path: &Path) -> Result, Error> { + Ok(Arc::new(Database::open(&self.config, &db_path.to_string_lossy())?)) + } + } + + Box::new(RestorationDBHandler { config }) +} diff --git a/ethcore/src/tests/client.rs b/ethcore/src/tests/client.rs index 2fee61412f21c1c8740c0de9d8b83b9e2821eafa..e18b4db98328d15e0c207e5f0d371db7b4b37321 100644 --- a/ethcore/src/tests/client.rs +++ b/ethcore/src/tests/client.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -23,19 +23,20 @@ use state::{self, State, CleanupMode}; use executive::{Executive, TransactOptions}; use ethereum; use block::IsBlock; -use tests::helpers::{ +use test_helpers::{ generate_dummy_client, push_blocks_to_client, get_test_client_with_blocks, get_good_dummy_block_seq, generate_dummy_client_with_data, get_good_dummy_block, get_bad_state_dummy_block }; use types::filter::Filter; use ethereum_types::{U256, Address}; use kvdb_rocksdb::{Database, DatabaseConfig}; -use miner::Miner; +use miner::{Miner, PendingOrdering}; use spec::Spec; use views::BlockView; use ethkey::KeyPair; use transaction::{PendingTransaction, Transaction, Action, Condition}; use miner::MinerService; +use rlp::{RlpStream, EMPTY_LIST_RLP}; use tempdir::TempDir; #[test] @@ -49,7 +50,7 @@ fn imports_from_empty() { ClientConfig::default(), &spec, client_db, - Arc::new(Miner::with_spec(&spec)), + Arc::new(Miner::new_for_tests(&spec, None)), IoChannel::disconnected(), ).unwrap(); client.import_verified_blocks(); @@ -67,7 +68,7 @@ fn should_return_registrar() { ClientConfig::default(), &spec, client_db, - Arc::new(Miner::with_spec(&spec)), + Arc::new(Miner::new_for_tests(&spec, None)), IoChannel::disconnected(), ).unwrap(); let params = client.additional_params(); @@ -97,7 +98,7 @@ fn imports_good_block() { ClientConfig::default(), &spec, client_db, - Arc::new(Miner::with_spec(&spec)), + Arc::new(Miner::new_for_tests(&spec, None)), IoChannel::disconnected(), ).unwrap(); let good_block = get_good_dummy_block(); @@ -111,6 +112,24 @@ fn imports_good_block() { assert!(!block.into_inner().is_empty()); } +#[test] +fn fails_to_import_block_with_invalid_rlp() { + use error::{BlockImportError, BlockImportErrorKind}; + + let client = generate_dummy_client(6); + let mut rlp = RlpStream::new_list(3); + rlp.append_raw(&EMPTY_LIST_RLP, 1); // empty header + rlp.append_raw(&EMPTY_LIST_RLP, 1); + rlp.append_raw(&EMPTY_LIST_RLP, 1); + let invalid_header_block = rlp.out(); + + match client.import_block(invalid_header_block) { + Err(BlockImportError(BlockImportErrorKind::Decoder(_), _)) => (), // all good + Err(_) => panic!("Should fail with a decoder error"), + Ok(_) => panic!("Should not import block with invalid header"), + } +} + #[test] fn query_none_block() { let tempdir = TempDir::new("").unwrap(); @@ -122,7 +141,7 @@ fn query_none_block() { ClientConfig::default(), &spec, client_db, - Arc::new(Miner::with_spec(&spec)), + Arc::new(Miner::new_for_tests(&spec, None)), IoChannel::disconnected(), ).unwrap(); let non_existant = client.block_header(BlockId::Number(188)); @@ -141,7 +160,7 @@ fn query_bad_block() { fn returns_chain_info() { let dummy_block = get_good_dummy_block(); let client = get_test_client_with_blocks(vec![dummy_block.clone()]); - let block = BlockView::new(&dummy_block); + let block = view!(BlockView, &dummy_block); let info = client.chain_info(); assert_eq!(info.best_block_hash, block.header().hash()); } @@ -178,12 +197,12 @@ fn returns_logs_with_limit() { fn returns_block_body() { let dummy_block = get_good_dummy_block(); let client = get_test_client_with_blocks(vec![dummy_block.clone()]); - let block = BlockView::new(&dummy_block); + let block = view!(BlockView, &dummy_block); let body = client.block_body(BlockId::Hash(block.header().hash())).unwrap(); let body = body.rlp(); - assert_eq!(body.item_count(), 2); - assert_eq!(body.at(0).as_raw()[..], block.rlp().at(1).as_raw()[..]); - assert_eq!(body.at(1).as_raw()[..], block.rlp().at(2).as_raw()[..]); + assert_eq!(body.item_count().unwrap(), 2); + assert_eq!(body.at(0).unwrap().as_raw()[..], block.rlp().at(1).as_raw()[..]); + assert_eq!(body.at(1).unwrap().as_raw()[..], block.rlp().at(2).as_raw()[..]); } #[test] @@ -201,7 +220,6 @@ fn can_collect_garbage() { assert!(client.blockchain_cache_info().blocks < 100 * 1024); } - #[test] fn can_generate_gas_price_median() { let client = generate_dummy_client_with_data(3, 1, slice_into![1, 2, 3]); @@ -259,7 +277,7 @@ fn can_mine() { let b = client.prepare_open_block(Address::default(), (3141562.into(), 31415620.into()), vec![]).close(); - assert_eq!(*b.block().header().parent_hash(), BlockView::new(&dummy_blocks[0]).header_view().hash()); + assert_eq!(*b.block().header().parent_hash(), view!(BlockView, &dummy_blocks[0]).header_view().hash()); } #[test] @@ -277,7 +295,7 @@ fn change_history_size() { ClientConfig::default(), &test_spec, client_db.clone(), - Arc::new(Miner::with_spec(&test_spec)), + Arc::new(Miner::new_for_tests(&test_spec, None)), IoChannel::disconnected() ).unwrap(); @@ -295,7 +313,7 @@ fn change_history_size() { config, &test_spec, client_db, - Arc::new(Miner::with_spec(&test_spec)), + Arc::new(Miner::new_for_tests(&test_spec, None)), IoChannel::disconnected(), ).unwrap(); assert_eq!(client.state().balance(&address).unwrap(), 100.into()); @@ -325,12 +343,12 @@ fn does_not_propagate_delayed_transactions() { client.miner().import_own_transaction(&*client, tx0).unwrap(); client.miner().import_own_transaction(&*client, tx1).unwrap(); - assert_eq!(0, client.ready_transactions().len()); - assert_eq!(2, client.miner().pending_transactions().len()); + assert_eq!(0, client.ready_transactions(10).len()); + assert_eq!(0, client.miner().ready_transactions(&*client, 10, PendingOrdering::Priority).len()); push_blocks_to_client(&client, 53, 2, 2); client.flush_queue(); - assert_eq!(2, client.ready_transactions().len()); - assert_eq!(2, client.miner().pending_transactions().len()); + assert_eq!(2, client.ready_transactions(10).len()); + assert_eq!(2, client.miner().ready_transactions(&*client, 10, PendingOrdering::Priority).len()); } #[test] @@ -362,7 +380,7 @@ fn transaction_proof() { let mut factories = ::factory::Factories::default(); factories.accountdb = ::account_db::Factory::Plain; // raw state values, no mangled keys. - let root = client.best_block_header().state_root(); + let root = *client.best_block_header().state_root(); let mut state = State::from_existing(backend, root, 0.into(), factories.clone()).unwrap(); Executive::new(&mut state, &client.latest_env_info(), test_spec.engine.machine()) diff --git a/ethcore/src/tests/evm.rs b/ethcore/src/tests/evm.rs index 39feb1364438d7b299b69ddc186d3bca7adb6bd6..4f4ad4241f2b1a39ff60c35ccc8a11fd9279dfc6 100644 --- a/ethcore/src/tests/evm.rs +++ b/ethcore/src/tests/evm.rs @@ -22,7 +22,7 @@ use vm::{EnvInfo, ActionParams, ActionValue, CallType, ParamsType}; use evm::{Factory, VMType}; use executive::Executive; use state::Substate; -use tests::helpers::get_temp_state_with_factory; +use test_helpers::get_temp_state_with_factory; use trace::{NoopVMTracer, NoopTracer}; use transaction::SYSTEM_ADDRESS; @@ -31,7 +31,7 @@ use rustc_hex::FromHex; use ethereum_types::{H256, Address}; use bytes::BytesRef; -evm_test!{test_blockhash_eip210: test_blockhash_eip210_jit, test_blockhash_eip210_int} +evm_test!{test_blockhash_eip210: test_blockhash_eip210_int} fn test_blockhash_eip210(factory: Factory) { let get_prev_hash_code = Arc::new("600143034060205260206020f3".from_hex().unwrap()); // this returns previous block hash let get_prev_hash_code_hash = keccak(get_prev_hash_code.as_ref()); diff --git a/ethcore/src/tests/mod.rs b/ethcore/src/tests/mod.rs index eec71efafbfc10355679f2c456223f96a8acb312..d1d5b6ef7fe9190e4a2217d2eb5d6c4988582952 100644 --- a/ethcore/src/tests/mod.rs +++ b/ethcore/src/tests/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -14,7 +14,6 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -pub mod helpers; mod client; mod evm; mod trace; diff --git a/ethcore/src/tests/trace.rs b/ethcore/src/tests/trace.rs index 5483c626342f60f7d2afb672da02991615a75b95..7071ef14866f3154ca5d5d69db5fc0d9a5357cd3 100644 --- a/ethcore/src/tests/trace.rs +++ b/ethcore/src/tests/trace.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -24,7 +24,7 @@ use ethereum_types::{U256, Address}; use io::*; use spec::*; use client::*; -use tests::helpers::get_temp_state_db; +use test_helpers::get_temp_state_db; use client::{BlockChainClient, Client, ClientConfig}; use kvdb_rocksdb::{Database, DatabaseConfig}; use std::sync::Arc; @@ -50,7 +50,7 @@ fn can_trace_block_and_uncle_reward() { client_config, &spec, client_db, - Arc::new(Miner::with_spec(&spec)), + Arc::new(Miner::new_for_tests(&spec, None)), IoChannel::disconnected(), ).unwrap(); @@ -87,6 +87,7 @@ fn can_trace_block_and_uncle_reward() { (3141562.into(), 31415620.into()), vec![], false, + &mut Vec::new().into_iter(), ).unwrap(); rolling_timestamp += 10; root_block.set_timestamp(rolling_timestamp); @@ -97,7 +98,7 @@ fn can_trace_block_and_uncle_reward() { panic!("error importing block which is valid by definition: {:?}", e); } - last_header = BlockView::new(&root_block.rlp_bytes()).header(); + last_header = view!(BlockView, &root_block.rlp_bytes()).header(); let root_header = last_header.clone(); db = root_block.drain(); @@ -115,6 +116,7 @@ fn can_trace_block_and_uncle_reward() { (3141562.into(), 31415620.into()), vec![], false, + &mut Vec::new().into_iter(), ).unwrap(); rolling_timestamp += 10; parent_block.set_timestamp(rolling_timestamp); @@ -125,7 +127,7 @@ fn can_trace_block_and_uncle_reward() { panic!("error importing block which is valid by definition: {:?}", e); } - last_header = BlockView::new(&parent_block.rlp_bytes()).header(); + last_header = view!(BlockView,&parent_block.rlp_bytes()).header(); db = parent_block.drain(); last_hashes.push(last_header.hash()); @@ -141,7 +143,8 @@ fn can_trace_block_and_uncle_reward() { author.clone(), (3141562.into(), 31415620.into()), vec![], - false + false, + &mut Vec::new().into_iter(), ).unwrap(); rolling_timestamp += 10; block.set_timestamp(rolling_timestamp); diff --git a/ethcore/src/trace/config.rs b/ethcore/src/trace/config.rs index dbd8a97affe1a4d587d5cf4385c6bae34f2d8def..e9b003adf9e5a997573220fa90a3a882d19b796c 100644 --- a/ethcore/src/trace/config.rs +++ b/ethcore/src/trace/config.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/src/trace/db.rs b/ethcore/src/trace/db.rs index 45b9ebc150acf02c4606edf029dd51129be00d13..29f294062c2df9cfdad3a8ac981140cf485d7399 100644 --- a/ethcore/src/trace/db.rs +++ b/ethcore/src/trace/db.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -590,7 +590,6 @@ mod tests { assert!(tracedb.traces(&block_0).is_some(), "Traces should be available even if block is non-canon."); } - #[test] fn test_import() { let db = new_db(); diff --git a/ethcore/src/trace/executive_tracer.rs b/ethcore/src/trace/executive_tracer.rs index b1d116d69d59accbbbd31a07e8442472a3a0ef1e..1bae15d5954542dcdf688794f2d6e9894b9d23e8 100644 --- a/ethcore/src/trace/executive_tracer.rs +++ b/ethcore/src/trace/executive_tracer.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/src/trace/import.rs b/ethcore/src/trace/import.rs index fb72e220e45fb91b9a9a8c7b646c9f50854a9a24..b720b0b86a91fb5af2135c4fe6d45851a93544a6 100644 --- a/ethcore/src/trace/import.rs +++ b/ethcore/src/trace/import.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/src/trace/mod.rs b/ethcore/src/trace/mod.rs index 381dcd9f0d186a51fff8baad461ec96d57baabb6..569b2a679106d362908c77059910ec7fc6e46bdd 100644 --- a/ethcore/src/trace/mod.rs +++ b/ethcore/src/trace/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/src/trace/noop_tracer.rs b/ethcore/src/trace/noop_tracer.rs index ab0bf77ff1cad8e676953151aacc33ed00aca3ec..8312de58f875fa66917928cfea017eee85c55e70 100644 --- a/ethcore/src/trace/noop_tracer.rs +++ b/ethcore/src/trace/noop_tracer.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/src/trace/types/error.rs b/ethcore/src/trace/types/error.rs index 70a3c315ad60938ed314b009112f2b8aee4f55b6..a934443c5dbe6823548dbba2f7bcbfbb9908e682 100644 --- a/ethcore/src/trace/types/error.rs +++ b/ethcore/src/trace/types/error.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -17,7 +17,7 @@ //! Trace errors. use std::fmt; -use rlp::{Encodable, RlpStream, Decodable, DecoderError, UntrustedRlp}; +use rlp::{Encodable, RlpStream, Decodable, DecoderError, Rlp}; use vm::Error as VmError; /// Trace evm errors. @@ -115,7 +115,7 @@ impl Encodable for Error { } impl Decodable for Error { - fn decode(rlp: &UntrustedRlp) -> Result { + fn decode(rlp: &Rlp) -> Result { use self::Error::*; let value: u8 = rlp.as_val()?; match value { diff --git a/ethcore/src/trace/types/filter.rs b/ethcore/src/trace/types/filter.rs index 308eb72da7b42700510989219b9aad34432c8e09..b3a5de58cd86b2e2b1c7d3ad7559349bc343d8c7 100644 --- a/ethcore/src/trace/types/filter.rs +++ b/ethcore/src/trace/types/filter.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/src/trace/types/flat.rs b/ethcore/src/trace/types/flat.rs index e97f4d32359b89c0c807b1341f3d4d295e5e5f64..8610692200c310c505ff3bfdcfbf9e4fb4821bfa 100644 --- a/ethcore/src/trace/types/flat.rs +++ b/ethcore/src/trace/types/flat.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -17,7 +17,7 @@ //! Flat trace module use std::collections::VecDeque; -use rlp::*; +use rlp::{Rlp, RlpStream, Decodable, Encodable, DecoderError}; use heapsize::HeapSizeOf; use ethereum_types::Bloom; use super::trace::{Action, Res}; @@ -63,7 +63,7 @@ impl Encodable for FlatTrace { } impl Decodable for FlatTrace { - fn decode(d: &UntrustedRlp) -> Result { + fn decode(d: &Rlp) -> Result { let v: Vec = d.list_at(3)?; let res = FlatTrace { action: d.val_at(0)?, @@ -244,7 +244,7 @@ mod tests { ]); let encoded = ::rlp::encode(&block_traces); - let decoded = ::rlp::decode(&encoded); + let decoded = ::rlp::decode(&encoded).expect("error decoding block traces"); assert_eq!(block_traces, decoded); } } diff --git a/ethcore/src/trace/types/localized.rs b/ethcore/src/trace/types/localized.rs index f649e169971fe62964507dd5742a79848182c3c1..816eccc93798ff76292d70066a485e256c7013fd 100644 --- a/ethcore/src/trace/types/localized.rs +++ b/ethcore/src/trace/types/localized.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/src/trace/types/mod.rs b/ethcore/src/trace/types/mod.rs index a9be2865b0fbf335dfb5ac30a8f1e81e9b76cc59..0e019ac552d451c60375ae25c015aa6ec06fdc0c 100644 --- a/ethcore/src/trace/types/mod.rs +++ b/ethcore/src/trace/types/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/src/trace/types/trace.rs b/ethcore/src/trace/types/trace.rs index 18fe329c442c99e894894c2ec91d90ab861d9e2e..1dde16e23baeeef04d4db531333cd431527869cb 100644 --- a/ethcore/src/trace/types/trace.rs +++ b/ethcore/src/trace/types/trace.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -18,7 +18,7 @@ use ethereum_types::{U256, Address, Bloom, BloomInput}; use bytes::Bytes; -use rlp::*; +use rlp::{Rlp, RlpStream, Encodable, DecoderError, Decodable}; use vm::ActionParams; use evm::CallType; @@ -141,6 +141,10 @@ pub enum RewardType { Block, /// Uncle Uncle, + /// Empty step (AuthorityRound) + EmptyStep, + /// A reward directly attributed by an external protocol (e.g. block reward contract) + External, } impl Encodable for RewardType { @@ -148,16 +152,20 @@ impl Encodable for RewardType { let v = match *self { RewardType::Block => 0u32, RewardType::Uncle => 1, + RewardType::EmptyStep => 2, + RewardType::External => 3, }; Encodable::rlp_append(&v, s); } } impl Decodable for RewardType { - fn decode(rlp: &UntrustedRlp) -> Result { + fn decode(rlp: &Rlp) -> Result { rlp.as_val().and_then(|v| Ok(match v { 0u32 => RewardType::Block, 1 => RewardType::Uncle, + 2 => RewardType::EmptyStep, + 3 => RewardType::External, _ => return Err(DecoderError::Custom("Invalid value of RewardType item")), })) } @@ -191,7 +199,7 @@ impl Encodable for Reward { } impl Decodable for Reward { - fn decode(rlp: &UntrustedRlp) -> Result { + fn decode(rlp: &Rlp) -> Result { let res = Reward { author: rlp.val_at(0)?, value: rlp.val_at(1)?, @@ -202,7 +210,6 @@ impl Decodable for Reward { } } - /// Suicide action. #[derive(Debug, Clone, PartialEq, RlpEncodable, RlpDecodable)] pub struct Suicide { @@ -263,7 +270,7 @@ impl Encodable for Action { } impl Decodable for Action { - fn decode(rlp: &UntrustedRlp) -> Result { + fn decode(rlp: &Rlp) -> Result { let action_type: u8 = rlp.val_at(0)?; match action_type { 0 => rlp.val_at(1).map(Action::Call), @@ -334,7 +341,7 @@ impl Encodable for Res { } impl Decodable for Res { - fn decode(rlp: &UntrustedRlp) -> Result { + fn decode(rlp: &Rlp) -> Result { let action_type: u8 = rlp.val_at(0)?; match action_type { 0 => rlp.val_at(1).map(Res::Call), diff --git a/ethcore/src/tx_filter.rs b/ethcore/src/tx_filter.rs index 12e6fe3ebbbf6470bc4eded8564663361d3f8535..12ddbb02a45d0f2d4cf501d226e063f08b0b9ea7 100644 --- a/ethcore/src/tx_filter.rs +++ b/ethcore/src/tx_filter.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -16,16 +16,16 @@ //! Smart contract based transaction filter. -use std::collections::HashMap; -use std::collections::hash_map::Entry; -use ethereum_types::{H256, Address}; -use client::{BlockInfo, CallContract, BlockId, ChainNotify}; -use bytes::Bytes; +use ethereum_types::{H256, U256, Address}; +use lru_cache::LruCache; + +use client::{BlockInfo, CallContract, BlockId}; use parking_lot::Mutex; use spec::CommonParams; use transaction::{Action, SignedTransaction}; use hash::KECCAK_EMPTY; +use_contract!(transact_acl_deprecated, "TransactAclDeprecated", "res/contracts/tx_acl_deprecated.json"); use_contract!(transact_acl, "TransactAcl", "res/contracts/tx_acl.json"); const MAX_CACHE_SIZE: usize = 4096; @@ -41,9 +41,11 @@ mod tx_permissions { /// Connection filter that uses a contract to manage permissions. pub struct TransactionFilter { + contract_deprecated: transact_acl_deprecated::TransactAclDeprecated, contract: transact_acl::TransactAcl, contract_address: Address, - permission_cache: Mutex>, + permission_cache: Mutex>, + contract_version_cache: Mutex>> } impl TransactionFilter { @@ -51,59 +53,87 @@ impl TransactionFilter { pub fn from_params(params: &CommonParams) -> Option { params.transaction_permission_contract.map(|address| TransactionFilter { + contract_deprecated: transact_acl_deprecated::TransactAclDeprecated::default(), contract: transact_acl::TransactAcl::default(), contract_address: address, - permission_cache: Mutex::new(HashMap::new()), + permission_cache: Mutex::new(LruCache::new(MAX_CACHE_SIZE)), + contract_version_cache: Mutex::new(LruCache::new(MAX_CACHE_SIZE)), } ) } - /// Clear cached permissions. - pub fn clear_cache(&self) { - self.permission_cache.lock().clear(); - } - /// Check if transaction is allowed at given block. pub fn transaction_allowed(&self, parent_hash: &H256, transaction: &SignedTransaction, client: &C) -> bool { - let mut cache = self.permission_cache.lock(); let len = cache.len(); + let mut permission_cache = self.permission_cache.lock(); + let mut contract_version_cache = self.contract_version_cache.lock(); - let tx_type = match transaction.action { - Action::Create => tx_permissions::CREATE, + let (tx_type, to) = match transaction.action { + Action::Create => (tx_permissions::CREATE, Address::new()), Action::Call(address) => if client.code_hash(&address, BlockId::Hash(*parent_hash)).map_or(false, |c| c != KECCAK_EMPTY) { - tx_permissions::CALL - } else { - tx_permissions::BASIC - } + (tx_permissions::CALL, address) + } else { + (tx_permissions::BASIC, address) + } }; + let sender = transaction.sender(); - match cache.entry((*parent_hash, sender)) { - Entry::Occupied(entry) => *entry.get() & tx_type != 0, - Entry::Vacant(entry) => { - let contract_address = self.contract_address; - let permissions = self.contract.functions() - .allowed_tx_types() - .call(sender, &|data| client.call_contract(BlockId::Hash(*parent_hash), contract_address, data)) - .map(|p| p.low_u32()) - .unwrap_or_else(|e| { - debug!("Error callling tx permissions contract: {:?}", e); - tx_permissions::NONE - }); - - if len < MAX_CACHE_SIZE { - entry.insert(permissions); + let value = transaction.value; + let key = (*parent_hash, sender); + + if let Some(permissions) = permission_cache.get_mut(&key) { + return *permissions & tx_type != 0; + } + + let contract_address = self.contract_address; + let contract_version = contract_version_cache.get_mut(parent_hash).and_then(|v| *v).or_else(|| { + self.contract.functions() + .contract_version() + .call(&|data| client.call_contract(BlockId::Hash(*parent_hash), contract_address, data)) + .ok() + }); + contract_version_cache.insert(*parent_hash, contract_version); + + // Check permissions in smart contract based on its version + let (permissions, filter_only_sender) = match contract_version { + Some(version) => { + let version_u64 = version.low_u64(); + trace!(target: "tx_filter", "Version of tx permission contract: {}", version); + match version_u64 { + 2 => self.contract.functions() + .allowed_tx_types() + .call(sender, to, value, &|data| client.call_contract(BlockId::Hash(*parent_hash), contract_address, data)) + .map(|(p, f)| (p.low_u32(), f)) + .unwrap_or_else(|e| { + error!(target: "tx_filter", "Error calling tx permissions contract: {:?}", e); + (tx_permissions::NONE, true) + }), + _ => { + error!(target: "tx_filter", "Unknown version of tx permissions contract is used"); + (tx_permissions::NONE, true) + } } - trace!("Permissions required: {}, got: {}", tx_type, permissions); - permissions & tx_type != 0 + }, + None => { + trace!(target: "tx_filter", "Fallback to the deprecated version of tx permission contract"); + (self.contract_deprecated.functions() + .allowed_tx_types() + .call(sender, &|data| client.call_contract(BlockId::Hash(*parent_hash), contract_address, data)) + .map(|p| p.low_u32()) + .unwrap_or_else(|e| { + error!(target: "tx_filter", "Error calling tx permissions contract: {:?}", e); + tx_permissions::NONE + }), true) } - } - } -} + }; -impl ChainNotify for TransactionFilter { - fn new_blocks(&self, imported: Vec, _invalid: Vec, _enacted: Vec, _retracted: Vec, _sealed: Vec, _proposed: Vec, _duration: u64) { - if !imported.is_empty() { - self.clear_cache(); + if filter_only_sender { + permission_cache.insert((*parent_hash, sender), permissions); } + trace!(target: "tx_filter", + "Given transaction data: sender: {:?} to: {:?} value: {}. Permissions required: {:X}, got: {:X}", + sender, to, value, tx_type, permissions + ); + permissions & tx_type != 0 } } @@ -113,61 +143,91 @@ mod test { use spec::Spec; use client::{BlockChainClient, Client, ClientConfig, BlockId}; use miner::Miner; - use ethereum_types::Address; + use ethereum_types::{U256, Address}; use io::IoChannel; use ethkey::{Secret, KeyPair}; use super::TransactionFilter; use transaction::{Transaction, Action}; use tempdir::TempDir; - /// Contract code: https://gist.github.com/arkpar/38a87cb50165b7e683585eec71acb05a + /// Contract code: https://gist.github.com/VladLupashevskyi/84f18eabb1e4afadf572cf92af3e7e7f #[test] fn transaction_filter() { - let spec_data = r#" - { - "name": "TestNodeFilterContract", - "engine": { - "authorityRound": { - "params": { - "stepDuration": 1, - "startStep": 2, - "validators": { - "contract": "0x0000000000000000000000000000000000000000" - } - } - } - }, - "params": { - "accountStartNonce": "0x0", - "maximumExtraDataSize": "0x20", - "minGasLimit": "0x1388", - "networkID" : "0x69", - "gasLimitBoundDivisor": "0x0400", - "transactionPermissionContract": "0x0000000000000000000000000000000000000005" - }, - "genesis": { - "seal": { - "generic": "0xc180" - }, - "difficulty": "0x20000", - "author": "0x0000000000000000000000000000000000000000", - "timestamp": "0x00", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "extraData": "0x", - "gasLimit": "0x222222" - }, - "accounts": { - "0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, - "0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, - "0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, - "0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, - "0000000000000000000000000000000000000005": { - "balance": "1", - "constructor": "6060604052341561000f57600080fd5b5b6101868061001f6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063e17512211461003e575b600080fd5b341561004957600080fd5b610075600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610097565b604051808263ffffffff1663ffffffff16815260200191505060405180910390f35b6000737e5f4552091a69125d5dfcb7b8c2659029395bdf8273ffffffffffffffffffffffffffffffffffffffff1614156100d75763ffffffff9050610155565b732b5ad5c4795c026514f8317c7a215e218dccd6cf8273ffffffffffffffffffffffffffffffffffffffff1614156101155760026001179050610155565b736813eb9362372eef6200f3b1dbc3f819671cba698273ffffffffffffffffffffffffffffffffffffffff1614156101505760019050610155565b600090505b9190505600a165627a7a72305820f1f21cb978925a8a92c6e30c8c81adf598adff6d1ef941cf5ed6c0ec7ad1ae3d0029" - } - } - } - "#; + let spec_data = include_str!("../res/tx_permission_tests/contract_ver_2_genesis.json"); + + let tempdir = TempDir::new("").unwrap(); + let spec = Spec::load(&tempdir.path(), spec_data.as_bytes()).unwrap(); + let client_db = Arc::new(::kvdb_memorydb::create(::db::NUM_COLUMNS.unwrap_or(0))); + + let client = Client::new( + ClientConfig::default(), + &spec, + client_db, + Arc::new(Miner::new_for_tests(&spec, None)), + IoChannel::disconnected(), + ).unwrap(); + let key1 = KeyPair::from_secret(Secret::from("0000000000000000000000000000000000000000000000000000000000000001")).unwrap(); + let key2 = KeyPair::from_secret(Secret::from("0000000000000000000000000000000000000000000000000000000000000002")).unwrap(); + let key3 = KeyPair::from_secret(Secret::from("0000000000000000000000000000000000000000000000000000000000000003")).unwrap(); + let key4 = KeyPair::from_secret(Secret::from("0000000000000000000000000000000000000000000000000000000000000004")).unwrap(); + let key5 = KeyPair::from_secret(Secret::from("0000000000000000000000000000000000000000000000000000000000000005")).unwrap(); + let key6 = KeyPair::from_secret(Secret::from("0000000000000000000000000000000000000000000000000000000000000006")).unwrap(); + let key7 = KeyPair::from_secret(Secret::from("0000000000000000000000000000000000000000000000000000000000000007")).unwrap(); + + let filter = TransactionFilter::from_params(spec.params()).unwrap(); + let mut basic_tx = Transaction::default(); + basic_tx.action = Action::Call(Address::from("d41c057fd1c78805aac12b0a94a405c0461a6fbb")); + let create_tx = Transaction::default(); + let mut call_tx = Transaction::default(); + call_tx.action = Action::Call(Address::from("0000000000000000000000000000000000000005")); + + let mut basic_tx_with_ether_and_to_key7 = Transaction::default(); + basic_tx_with_ether_and_to_key7.action = Action::Call(Address::from("d41c057fd1c78805aac12b0a94a405c0461a6fbb")); + basic_tx_with_ether_and_to_key7.value = U256::from(123123); + let mut call_tx_with_ether = Transaction::default(); + call_tx_with_ether.action = Action::Call(Address::from("0000000000000000000000000000000000000005")); + call_tx_with_ether.value = U256::from(123123); + + let mut basic_tx_to_key6 = Transaction::default(); + basic_tx_to_key6.action = Action::Call(Address::from("e57bfe9f44b819898f47bf37e5af72a0783e1141")); + let mut basic_tx_with_ether_and_to_key6 = Transaction::default(); + basic_tx_with_ether_and_to_key6.action = Action::Call(Address::from("e57bfe9f44b819898f47bf37e5af72a0783e1141")); + basic_tx_with_ether_and_to_key6.value = U256::from(123123); + + let genesis = client.block_hash(BlockId::Latest).unwrap(); + + assert!(filter.transaction_allowed(&genesis, &basic_tx.clone().sign(key1.secret(), None), &*client)); + assert!(filter.transaction_allowed(&genesis, &create_tx.clone().sign(key1.secret(), None), &*client)); + assert!(filter.transaction_allowed(&genesis, &call_tx.clone().sign(key1.secret(), None), &*client)); + + assert!(filter.transaction_allowed(&genesis, &basic_tx.clone().sign(key2.secret(), None), &*client)); + assert!(!filter.transaction_allowed(&genesis, &create_tx.clone().sign(key2.secret(), None), &*client)); + assert!(filter.transaction_allowed(&genesis, &call_tx.clone().sign(key2.secret(), None), &*client)); + + assert!(filter.transaction_allowed(&genesis, &basic_tx.clone().sign(key3.secret(), None), &*client)); + assert!(!filter.transaction_allowed(&genesis, &create_tx.clone().sign(key3.secret(), None), &*client)); + assert!(!filter.transaction_allowed(&genesis, &call_tx.clone().sign(key3.secret(), None), &*client)); + + assert!(!filter.transaction_allowed(&genesis, &basic_tx.clone().sign(key4.secret(), None), &*client)); + assert!(!filter.transaction_allowed(&genesis, &create_tx.clone().sign(key4.secret(), None), &*client)); + assert!(!filter.transaction_allowed(&genesis, &call_tx.clone().sign(key4.secret(), None), &*client)); + + assert!(filter.transaction_allowed(&genesis, &basic_tx.clone().sign(key1.secret(), None), &*client)); + assert!(filter.transaction_allowed(&genesis, &create_tx.clone().sign(key1.secret(), None), &*client)); + assert!(filter.transaction_allowed(&genesis, &call_tx.clone().sign(key1.secret(), None), &*client)); + + assert!(!filter.transaction_allowed(&genesis, &basic_tx_with_ether_and_to_key7.clone().sign(key5.secret(), None), &*client)); + assert!(!filter.transaction_allowed(&genesis, &call_tx_with_ether.clone().sign(key5.secret(), None), &*client)); + assert!(filter.transaction_allowed(&genesis, &basic_tx.clone().sign(key6.secret(), None), &*client)); + assert!(filter.transaction_allowed(&genesis, &basic_tx_with_ether_and_to_key7.clone().sign(key6.secret(), None), &*client)); + assert!(filter.transaction_allowed(&genesis, &basic_tx_to_key6.clone().sign(key7.secret(), None), &*client)); + assert!(!filter.transaction_allowed(&genesis, &basic_tx_with_ether_and_to_key6.clone().sign(key7.secret(), None), &*client)); + } + + /// Contract code: https://gist.github.com/arkpar/38a87cb50165b7e683585eec71acb05a + #[test] + fn transaction_filter_deprecated() { + let spec_data = include_str!("../res/tx_permission_tests/deprecated_contract_genesis.json"); let tempdir = TempDir::new("").unwrap(); let spec = Spec::load(&tempdir.path(), spec_data.as_bytes()).unwrap(); @@ -177,7 +237,7 @@ mod test { ClientConfig::default(), &spec, client_db, - Arc::new(Miner::with_spec(&spec)), + Arc::new(Miner::new_for_tests(&spec, None)), IoChannel::disconnected(), ).unwrap(); let key1 = KeyPair::from_secret(Secret::from("0000000000000000000000000000000000000000000000000000000000000001")).unwrap(); @@ -211,4 +271,3 @@ mod test { assert!(!filter.transaction_allowed(&genesis, &call_tx.clone().sign(key4.secret(), None), &*client)); } } - diff --git a/ethcore/src/verification/canon_verifier.rs b/ethcore/src/verification/canon_verifier.rs index 3d0fd77c6e5bc906a51be6a964365b76ef64293c..0ace8987e0b01c8ef5f26283d24f03292fcccdbd 100644 --- a/ethcore/src/verification/canon_verifier.rs +++ b/ethcore/src/verification/canon_verifier.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/src/verification/mod.rs b/ethcore/src/verification/mod.rs index d5fd4e847610fbcda6f5e844283070955fbc6bb9..ed4227ee21ee83cc19c09f838a2e6a26f18701d8 100644 --- a/ethcore/src/verification/mod.rs +++ b/ethcore/src/verification/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/src/verification/noop_verifier.rs b/ethcore/src/verification/noop_verifier.rs index 24b117bbc16afbfe19801cce3912741525adc535..d04eec9b113cfc0c55bc5088a14009b39e28e8ed 100644 --- a/ethcore/src/verification/noop_verifier.rs +++ b/ethcore/src/verification/noop_verifier.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/src/verification/queue/kind.rs b/ethcore/src/verification/queue/kind.rs index b437dba7a047b24b135b5b04397ea62ac6f67af8..2d89f11a33d6389f259cfeefc72a53481a2bf73e 100644 --- a/ethcore/src/verification/queue/kind.rs +++ b/ethcore/src/verification/queue/kind.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -69,7 +69,7 @@ pub mod blocks { use super::{Kind, BlockLike}; use engines::EthEngine; - use error::Error; + use error::{Error, ErrorKind, BlockError}; use header::Header; use verification::{PreverifiedBlock, verify_block_basic, verify_block_unordered}; @@ -88,6 +88,10 @@ pub mod blocks { fn create(input: Self::Input, engine: &EthEngine) -> Result { match verify_block_basic(&input.header, &input.bytes, engine) { Ok(()) => Ok(input), + Err(Error(ErrorKind::Block(BlockError::TemporarilyInvalid(oob)), _)) => { + debug!(target: "client", "Block received too early {}: {:?}", input.hash(), oob); + Err(BlockError::TemporarilyInvalid(oob).into()) + }, Err(e) => { warn!(target: "client", "Stage 1 block verification failed for {}: {:?}", input.hash(), e); Err(e) @@ -115,14 +119,13 @@ pub mod blocks { impl Unverified { /// Create an `Unverified` from raw bytes. - pub fn new(bytes: Bytes) -> Self { - use views::BlockView; + pub fn from_rlp(bytes: Bytes) -> Result { - let header = BlockView::new(&bytes).header(); - Unverified { + let header = ::rlp::Rlp::new(&bytes).val_at(0)?; + Ok(Unverified { header: header, bytes: bytes, - } + }) } } diff --git a/ethcore/src/verification/queue/mod.rs b/ethcore/src/verification/queue/mod.rs index 58b1bb7bb3cf1ae906dcbb015fbb061eb544d8eb..5ae4f7c8fccf08888d86d66e3f49d3f8575003d0 100644 --- a/ethcore/src/verification/queue/mod.rs +++ b/ethcore/src/verification/queue/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -472,17 +472,17 @@ impl VerificationQueue { let h = input.hash(); { if self.processing.read().contains_key(&h) { - return Err(ImportError::AlreadyQueued.into()); + bail!(ErrorKind::Import(ImportErrorKind::AlreadyQueued)); } let mut bad = self.verification.bad.lock(); if bad.contains(&h) { - return Err(ImportError::KnownBad.into()); + bail!(ErrorKind::Import(ImportErrorKind::KnownBad)); } if bad.contains(&input.parent_hash()) { bad.insert(h.clone()); - return Err(ImportError::KnownBad.into()); + bail!(ErrorKind::Import(ImportErrorKind::KnownBad)); } } @@ -502,7 +502,7 @@ impl VerificationQueue { Err(err) => { match err { // Don't mark future blocks as bad. - Error::Block(BlockError::TemporarilyInvalid(_)) => {}, + Error(ErrorKind::Block(BlockError::TemporarilyInvalid(_)), _) => {}, _ => { self.verification.bad.lock().insert(h.clone()); } @@ -731,9 +731,10 @@ mod tests { use spec::Spec; use super::{BlockQueue, Config, State}; use super::kind::blocks::Unverified; - use tests::helpers::{get_good_dummy_block_seq, get_good_dummy_block}; + use test_helpers::{get_good_dummy_block_seq, get_good_dummy_block}; use error::*; - use views::*; + use views::BlockView; + use bytes::Bytes; // create a test block queue. // auto_scaling enables verifier adjustment. @@ -746,6 +747,10 @@ mod tests { BlockQueue::new(config, engine, IoChannel::disconnected(), true) } + fn new_unverified(bytes: Bytes) -> Unverified { + Unverified::from_rlp(bytes).expect("Should be valid rlp") + } + #[test] fn can_be_created() { // TODO better test @@ -757,7 +762,7 @@ mod tests { #[test] fn can_import_blocks() { let queue = get_test_queue(false); - if let Err(e) = queue.import(Unverified::new(get_good_dummy_block())) { + if let Err(e) = queue.import(new_unverified(get_good_dummy_block())) { panic!("error importing block that is valid by definition({:?})", e); } } @@ -765,15 +770,15 @@ mod tests { #[test] fn returns_error_for_duplicates() { let queue = get_test_queue(false); - if let Err(e) = queue.import(Unverified::new(get_good_dummy_block())) { + if let Err(e) = queue.import(new_unverified(get_good_dummy_block())) { panic!("error importing block that is valid by definition({:?})", e); } - let duplicate_import = queue.import(Unverified::new(get_good_dummy_block())); + let duplicate_import = queue.import(new_unverified(get_good_dummy_block())); match duplicate_import { Err(e) => { match e { - Error::Import(ImportError::AlreadyQueued) => {}, + Error(ErrorKind::Import(ImportErrorKind::AlreadyQueued), _) => {}, _ => { panic!("must return AlreadyQueued error"); } } } @@ -785,8 +790,8 @@ mod tests { fn returns_total_difficulty() { let queue = get_test_queue(false); let block = get_good_dummy_block(); - let hash = BlockView::new(&block).header().hash().clone(); - if let Err(e) = queue.import(Unverified::new(block)) { + let hash = view!(BlockView, &block).header().hash().clone(); + if let Err(e) = queue.import(new_unverified(block)) { panic!("error importing block that is valid by definition({:?})", e); } queue.flush(); @@ -801,15 +806,15 @@ mod tests { fn returns_ok_for_drained_duplicates() { let queue = get_test_queue(false); let block = get_good_dummy_block(); - let hash = BlockView::new(&block).header().hash().clone(); - if let Err(e) = queue.import(Unverified::new(block)) { + let hash = view!(BlockView, &block).header().hash().clone(); + if let Err(e) = queue.import(new_unverified(block)) { panic!("error importing block that is valid by definition({:?})", e); } queue.flush(); queue.drain(10); queue.mark_as_good(&[ hash ]); - if let Err(e) = queue.import(Unverified::new(get_good_dummy_block())) { + if let Err(e) = queue.import(new_unverified(get_good_dummy_block())) { panic!("error importing block that has already been drained ({:?})", e); } } @@ -817,7 +822,7 @@ mod tests { #[test] fn returns_empty_once_finished() { let queue = get_test_queue(false); - queue.import(Unverified::new(get_good_dummy_block())) + queue.import(new_unverified(get_good_dummy_block())) .expect("error importing block that is valid by definition"); queue.flush(); queue.drain(1); @@ -835,7 +840,7 @@ mod tests { assert!(!queue.queue_info().is_full()); let mut blocks = get_good_dummy_block_seq(50); for b in blocks.drain(..) { - queue.import(Unverified::new(b)).unwrap(); + queue.import(new_unverified(b)).unwrap(); } assert!(queue.queue_info().is_full()); } @@ -863,7 +868,7 @@ mod tests { *queue.state.0.lock() = State::Work(0); for block in get_good_dummy_block_seq(5000) { - queue.import(Unverified::new(block)).expect("Block good by definition; qed"); + queue.import(new_unverified(block)).expect("Block good by definition; qed"); } // almost all unverified == bump verifier count. diff --git a/ethcore/src/verification/verification.rs b/ethcore/src/verification/verification.rs index f78059ac8dbf25422f2011de9f6346b608ab63c4..1275b5c5c3f8e028d18ef3df55fd3ef1e6f710e3 100644 --- a/ethcore/src/verification/verification.rs +++ b/ethcore/src/verification/verification.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -22,13 +22,13 @@ //! 3. Final verification against the blockchain done before enactment. use std::collections::HashSet; -use std::time::{SystemTime, UNIX_EPOCH}; +use std::time::{Duration, SystemTime, UNIX_EPOCH}; use bytes::Bytes; -use ethereum_types::{H256, U256}; +use ethereum_types::H256; use hash::keccak; use heapsize::HeapSizeOf; -use rlp::UntrustedRlp; +use rlp::Rlp; use triehash::ordered_trie_root; use unexpected::{Mismatch, OutOfBounds}; @@ -63,13 +63,13 @@ pub fn verify_block_basic(header: &Header, bytes: &[u8], engine: &EthEngine) -> verify_header_params(&header, engine, true)?; verify_block_integrity(bytes, &header.transactions_root(), &header.uncles_hash())?; engine.verify_block_basic(&header)?; - for u in UntrustedRlp::new(bytes).at(2)?.iter().map(|rlp| rlp.as_val::
()) { + for u in Rlp::new(bytes).at(2)?.iter().map(|rlp| rlp.as_val::
()) { let u = u?; verify_header_params(&u, engine, false)?; engine.verify_block_basic(&u)?; } - for t in UntrustedRlp::new(bytes).at(1)?.iter().map(|rlp| rlp.as_val::()) { + for t in Rlp::new(bytes).at(1)?.iter().map(|rlp| rlp.as_val::()) { engine.verify_transaction_basic(&t?, &header)?; } Ok(()) @@ -81,7 +81,7 @@ pub fn verify_block_basic(header: &Header, bytes: &[u8], engine: &EthEngine) -> pub fn verify_block_unordered(header: Header, bytes: Bytes, engine: &EthEngine, check_seal: bool) -> Result { if check_seal { engine.verify_block_unordered(&header)?; - for u in UntrustedRlp::new(&bytes).at(2)?.iter().map(|rlp| rlp.as_val::
()) { + for u in Rlp::new(&bytes).at(2)?.iter().map(|rlp| rlp.as_val::
()) { engine.verify_block_unordered(&u?)?; } } @@ -91,7 +91,7 @@ pub fn verify_block_unordered(header: Header, bytes: Bytes, engine: &EthEngine, Some((engine.params().nonce_cap_increment * header.number()).into()) } else { None }; { - let v = BlockView::new(&bytes); + let v = view!(BlockView, &bytes); for t in v.transactions() { let t = engine.verify_transaction_unordered(t, &header)?; if let Some(max_nonce) = nonce_cap { @@ -127,7 +127,7 @@ pub struct FullFamilyParams<'a, C: BlockInfo + CallContract + 'a> { /// Phase 3 verification. Check block information against parent and uncles. pub fn verify_block_family(header: &Header, parent: &Header, engine: &EthEngine, do_full: Option>) -> Result<(), Error> { // TODO: verify timestamp - verify_parent(&header, &parent, engine.params().gas_limit_bound_divisor)?; + verify_parent(&header, &parent, engine)?; engine.verify_block_family(&header, &parent)?; let params = match do_full { @@ -145,7 +145,7 @@ pub fn verify_block_family(header: &Header, parent: } fn verify_uncles(header: &Header, bytes: &[u8], bc: &BlockProvider, engine: &EthEngine) -> Result<(), Error> { - let num_uncles = UntrustedRlp::new(bytes).at(2)?.item_count()?; + let num_uncles = Rlp::new(bytes).at(2)?.item_count()?; let max_uncles = engine.maximum_uncle_count(header.number()); if num_uncles != 0 { if num_uncles > max_uncles { @@ -174,7 +174,7 @@ fn verify_uncles(header: &Header, bytes: &[u8], bc: &BlockProvider, engine: &Eth } let mut verified = HashSet::new(); - for uncle in UntrustedRlp::new(bytes).at(2)?.iter().map(|rlp| rlp.as_val::
()) { + for uncle in Rlp::new(bytes).at(2)?.iter().map(|rlp| rlp.as_val::
()) { let uncle = uncle?; if excluded.contains(&uncle.hash()) { return Err(From::from(BlockError::UncleInChain(uncle.hash()))) @@ -211,7 +211,7 @@ fn verify_uncles(header: &Header, bytes: &[u8], bc: &BlockProvider, engine: &Eth // cB.p^7 -------------/ // cB.p^8 let mut expected_uncle_parent = header.parent_hash().clone(); - let uncle_parent = bc.block_header(&uncle.parent_hash()).ok_or_else(|| Error::from(BlockError::UnknownUncleParent(uncle.parent_hash().clone())))?; + let uncle_parent = bc.block_header_data(&uncle.parent_hash()).ok_or_else(|| Error::from(BlockError::UnknownUncleParent(uncle.parent_hash().clone())))?; for _ in 0..depth { match bc.block_details(&expected_uncle_parent) { Some(details) => { @@ -224,7 +224,8 @@ fn verify_uncles(header: &Header, bytes: &[u8], bc: &BlockProvider, engine: &Eth return Err(From::from(BlockError::UncleParentNotInChain(uncle_parent.hash()))); } - verify_parent(&uncle, &uncle_parent, engine.params().gas_limit_bound_divisor)?; + let uncle_parent = uncle_parent.decode()?; + verify_parent(&uncle, &uncle_parent, engine)?; engine.verify_block_family(&uncle, &uncle_parent)?; verified.insert(uncle.hash()); } @@ -283,11 +284,10 @@ pub fn verify_header_params(header: &Header, engine: &EthEngine, is_full: bool) } if is_full { - const ACCEPTABLE_DRIFT_SECS: u64 = 15; - let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default(); - let max_time = now.as_secs() + ACCEPTABLE_DRIFT_SECS; - let invalid_threshold = max_time + ACCEPTABLE_DRIFT_SECS * 9; - let timestamp = header.timestamp(); + const ACCEPTABLE_DRIFT: Duration = Duration::from_secs(15); + let max_time = SystemTime::now() + ACCEPTABLE_DRIFT; + let invalid_threshold = max_time + ACCEPTABLE_DRIFT * 9; + let timestamp = UNIX_EPOCH + Duration::from_secs(header.timestamp()); if timestamp > invalid_threshold { return Err(From::from(BlockError::InvalidTimestamp(OutOfBounds { max: Some(max_time), min: None, found: timestamp }))) @@ -302,12 +302,16 @@ pub fn verify_header_params(header: &Header, engine: &EthEngine, is_full: bool) } /// Check header parameters agains parent header. -fn verify_parent(header: &Header, parent: &Header, gas_limit_divisor: U256) -> Result<(), Error> { - if !header.parent_hash().is_zero() && &parent.hash() != header.parent_hash() { - return Err(From::from(BlockError::InvalidParentHash(Mismatch { expected: parent.hash(), found: header.parent_hash().clone() }))) - } - if header.timestamp() <= parent.timestamp() { - return Err(From::from(BlockError::InvalidTimestamp(OutOfBounds { max: None, min: Some(parent.timestamp() + 1), found: header.timestamp() }))) +fn verify_parent(header: &Header, parent: &Header, engine: &EthEngine) -> Result<(), Error> { + assert!(header.parent_hash().is_zero() || &parent.hash() == header.parent_hash(), + "Parent hash should already have been verified; qed"); + + let gas_limit_divisor = engine.params().gas_limit_bound_divisor; + + if !engine.is_timestamp_valid(header.timestamp(), parent.timestamp()) { + let min = SystemTime::now() + Duration::from_secs(parent.timestamp() + 1); + let found = SystemTime::now() + Duration::from_secs(header.timestamp()); + return Err(From::from(BlockError::InvalidTimestamp(OutOfBounds { max: None, min: Some(min), found }))) } if header.number() != parent.number() + 1 { return Err(From::from(BlockError::InvalidNumber(Mismatch { expected: parent.number() + 1, found: header.number() }))); @@ -329,7 +333,7 @@ fn verify_parent(header: &Header, parent: &Header, gas_limit_divisor: U256) -> R /// Verify block data against header: transactions root and uncles hash. fn verify_block_integrity(block: &[u8], transactions_root: &H256, uncles_hash: &H256) -> Result<(), Error> { - let block = UntrustedRlp::new(block); + let block = Rlp::new(block); let tx = block.at(1)?; let expected_root = &ordered_trie_root(tx.iter().map(|r| r.as_raw())); if expected_root != transactions_root { @@ -354,9 +358,10 @@ mod tests { use hash::keccak; use engines::EthEngine; use error::BlockError::*; + use error::ErrorKind; use ethkey::{Random, Generator}; use spec::{CommonParams, Spec}; - use tests::helpers::{create_test_block_with_data, create_test_block}; + use test_helpers::{create_test_block_with_data, create_test_block}; use transaction::{SignedTransaction, Transaction, UnverifiedTransaction, Action}; use types::log_entry::{LogEntry, LocalizedLogEntry}; use rlp; @@ -368,7 +373,7 @@ mod tests { fn check_fail(result: Result<(), Error>, e: BlockError) { match result { - Err(Error::Block(ref error)) if *error == e => (), + Err(Error(ErrorKind::Block(ref error), _)) if *error == e => (), Err(other) => panic!("Block verification failed.\nExpected: {:?}\nGot: {:?}", e, other), Ok(_) => panic!("Block verification failed.\nExpected: {:?}\nGot: Ok", e), } @@ -377,8 +382,8 @@ mod tests { fn check_fail_timestamp(result: Result<(), Error>, temp: bool) { let name = if temp { "TemporarilyInvalid" } else { "InvalidTimestamp" }; match result { - Err(Error::Block(BlockError::InvalidTimestamp(_))) if !temp => (), - Err(Error::Block(BlockError::TemporarilyInvalid(_))) if temp => (), + Err(Error(ErrorKind::Block(BlockError::InvalidTimestamp(_)), _)) if !temp => (), + Err(Error(ErrorKind::Block(BlockError::TemporarilyInvalid(_)), _)) if temp => (), Err(other) => panic!("Block verification failed.\nExpected: {}\nGot: {:?}", name, other), Ok(_) => panic!("Block verification failed.\nExpected: {}\nGot: Ok", name), } @@ -404,8 +409,8 @@ mod tests { } pub fn insert(&mut self, bytes: Bytes) { - let number = BlockView::new(&bytes).header_view().number(); - let hash = BlockView::new(&bytes).header_view().hash(); + let number = view!(BlockView, &bytes).header_view().number(); + let hash = view!(BlockView, &bytes).header_view().hash(); self.blocks.insert(hash.clone(), bytes); self.numbers.insert(number, hash.clone()); } @@ -444,12 +449,14 @@ mod tests { /// Get the familial details concerning a block. fn block_details(&self, hash: &H256) -> Option { self.blocks.get(hash).map(|bytes| { - let header = BlockView::new(bytes).header(); + let header = view!(BlockView, bytes).header(); BlockDetails { number: header.number(), total_difficulty: header.difficulty().clone(), parent: header.parent_hash().clone(), children: Vec::new(), + is_finalized: false, + metadata: None, } }) } @@ -471,19 +478,19 @@ mod tests { unimplemented!() } - fn logs(&self, _blocks: Vec, _matches: F, _limit: Option) -> Vec + fn logs(&self, _blocks: Vec, _matches: F, _limit: Option) -> Vec where F: Fn(&LogEntry) -> bool, Self: Sized { unimplemented!() } } fn basic_test(bytes: &[u8], engine: &EthEngine) -> Result<(), Error> { - let header = BlockView::new(bytes).header(); + let header = view!(BlockView, bytes).header(); verify_block_basic(&header, bytes, engine) } fn family_test(bytes: &[u8], engine: &EthEngine, bc: &BC) -> Result<(), Error> where BC: BlockProvider { - let view = BlockView::new(bytes); + let view = view!(BlockView, bytes); let header = view.header(); let transactions: Vec<_> = view.transactions() .into_iter() @@ -495,9 +502,9 @@ mod tests { // no existing tests need access to test, so having this not function // is fine. let client = ::client::TestBlockChainClient::default(); - - let parent = bc.block_header(header.parent_hash()) - .ok_or(BlockError::UnknownParent(header.parent_hash().clone()))?; + let parent = bc.block_header_data(header.parent_hash()) + .ok_or(BlockError::UnknownParent(header.parent_hash().clone()))? + .decode()?; let full_params = FullFamilyParams { block_bytes: bytes, @@ -509,7 +516,7 @@ mod tests { } fn unordered_test(bytes: &[u8], engine: &EthEngine) -> Result<(), Error> { - let header = BlockView::new(bytes).header(); + let header = view!(BlockView, bytes).header(); verify_block_unordered(header, bytes.to_vec(), engine, false)?; Ok(()) } @@ -569,7 +576,17 @@ mod tests { nonce: U256::from(2) }.sign(keypair.secret(), None); + let tr3 = Transaction { + action: Action::Call(0x0.into()), + value: U256::from(0), + data: Bytes::new(), + gas: U256::from(30_000), + gas_price: U256::from(0), + nonce: U256::zero(), + }.null_sign(0); + let good_transactions = [ tr1.clone(), tr2.clone() ]; + let eip86_transactions = [ tr3.clone() ]; let diff_inc = U256::from(0x40); @@ -605,6 +622,7 @@ mod tests { uncles_rlp.append_list(&good_uncles); let good_uncles_hash = keccak(uncles_rlp.as_raw()); let good_transactions_root = ordered_trie_root(good_transactions.iter().map(|t| ::rlp::encode::(t))); + let eip86_transactions_root = ordered_trie_root(eip86_transactions.iter().map(|t| ::rlp::encode::(t))); let mut parent = good.clone(); parent.set_number(9); @@ -625,6 +643,14 @@ mod tests { check_ok(basic_test(&create_test_block(&good), engine)); + let mut bad_header = good.clone(); + bad_header.set_transactions_root(eip86_transactions_root.clone()); + bad_header.set_uncles_hash(good_uncles_hash.clone()); + match basic_test(&create_test_block_with_data(&bad_header, &eip86_transactions, &good_uncles), engine) { + Err(Error(ErrorKind::Transaction(ref e), _)) if e == &::ethkey::Error::InvalidSignature.into() => (), + e => panic!("Block verification failed.\nExpected: Transaction Error (Invalid Signature)\nGot: {:?}", e), + } + let mut header = good.clone(); header.set_transactions_root(good_transactions_root.clone()); header.set_uncles_hash(good_uncles_hash.clone()); @@ -675,8 +701,7 @@ mod tests { header = good.clone(); header.set_timestamp(10); - check_fail(family_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine, &bc), - InvalidTimestamp(OutOfBounds { max: None, min: Some(parent.timestamp() + 1), found: header.timestamp() })); + check_fail_timestamp(family_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine, &bc), false); header = good.clone(); header.set_timestamp(2450000000); @@ -712,7 +737,7 @@ mod tests { header.set_gas_limit(0.into()); header.set_difficulty("0000000000000000000000000000000000000000000000000000000000020000".parse::().unwrap()); match family_test(&create_test_block(&header), engine, &bc) { - Err(Error::Block(InvalidGasLimit(_))) => {}, + Err(Error(ErrorKind::Block(InvalidGasLimit(_)), _)) => {}, Err(_) => { panic!("should be invalid difficulty fail"); }, _ => { panic!("Should be error, got Ok"); }, } diff --git a/ethcore/src/verification/verifier.rs b/ethcore/src/verification/verifier.rs index a9ca22a4c8aa8534cd7fcb07c112606408cb361e..188254b4317fcbca14662817337d0c133ab67885 100644 --- a/ethcore/src/verification/verifier.rs +++ b/ethcore/src/verification/verifier.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/src/views/block.rs b/ethcore/src/views/block.rs index c60cc07c5425581304521f05d3f2d59d120d5399..2a7c2ebd5310c87637e98f8e3a87df97f660c71d 100644 --- a/ethcore/src/views/block.rs +++ b/ethcore/src/views/block.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -20,25 +20,33 @@ use bytes::Bytes; use ethereum_types::H256; use hash::keccak; use header::Header; -use rlp::Rlp; use transaction::{UnverifiedTransaction, LocalizedTransaction}; use views::{TransactionView, HeaderView}; +use super::ViewRlp; /// View onto block rlp. pub struct BlockView<'a> { - rlp: Rlp<'a> + rlp: ViewRlp<'a> } impl<'a> BlockView<'a> { - /// Creates new view onto block from raw bytes. - pub fn new(bytes: &'a [u8]) -> BlockView<'a> { - BlockView { - rlp: Rlp::new(bytes) - } - } - /// Creates new view onto block from rlp. - pub fn new_from_rlp(rlp: Rlp<'a>) -> BlockView<'a> { + /// Use the `view!` macro to create this view in order to capture debugging info. + /// + /// # Example + /// + /// ``` + /// #[macro_use] + /// extern crate ethcore; + /// + /// use ethcore::views::{BlockView}; + /// + /// fn main() { + /// let bytes : &[u8] = &[]; + /// let block_view = view!(BlockView, bytes); + /// } + /// ``` + pub fn new(rlp: ViewRlp<'a>) -> BlockView<'a> { BlockView { rlp: rlp } @@ -50,7 +58,7 @@ impl<'a> BlockView<'a> { } /// Return reference to underlaying rlp. - pub fn rlp(&self) -> &Rlp<'a> { + pub fn rlp(&self) -> &ViewRlp<'a> { &self.rlp } @@ -60,13 +68,13 @@ impl<'a> BlockView<'a> { } /// Return header rlp. - pub fn header_rlp(&self) -> Rlp { + pub fn header_rlp(&self) -> ViewRlp<'a> { self.rlp.at(0) } /// Create new header view obto block head rlp. pub fn header_view(&self) -> HeaderView<'a> { - HeaderView::new_from_rlp(self.rlp.at(0)) + HeaderView::new(self.header_rlp()) } /// Return List of transactions in given block. @@ -91,24 +99,29 @@ impl<'a> BlockView<'a> { }).collect() } + /// Return the raw rlp for the transactions in the given block. + pub fn transactions_rlp(&self) -> ViewRlp<'a> { + self.rlp.at(1) + } + /// Return number of transactions in given block, without deserializing them. pub fn transactions_count(&self) -> usize { - self.rlp.at(1).iter().count() + self.transactions_rlp().iter().count() } /// Return List of transactions in given block. pub fn transaction_views(&self) -> Vec> { - self.rlp.at(1).iter().map(TransactionView::new_from_rlp).collect() + self.transactions_rlp().iter().map(TransactionView::new).collect() } /// Return transaction hashes. pub fn transaction_hashes(&self) -> Vec { - self.rlp.at(1).iter().map(|rlp| keccak(rlp.as_raw())).collect() + self.transactions_rlp().iter().map(|rlp| keccak(rlp.as_raw())).collect() } /// Returns transaction at given index without deserializing unnecessary data. pub fn transaction_at(&self, index: usize) -> Option { - self.rlp.at(1).iter().nth(index).map(|rlp| rlp.as_val()) + self.transactions_rlp().iter().nth(index).map(|rlp| rlp.as_val()) } /// Returns localized transaction at given index. @@ -125,6 +138,11 @@ impl<'a> BlockView<'a> { }) } + /// Returns raw rlp for the uncles in the given block + pub fn uncles_rlp(&self) -> ViewRlp<'a> { + self.rlp.at(2) + } + /// Return list of uncles of given block. pub fn uncles(&self) -> Vec
{ self.rlp.list_at(2) @@ -132,27 +150,27 @@ impl<'a> BlockView<'a> { /// Return number of uncles in given block, without deserializing them. pub fn uncles_count(&self) -> usize { - self.rlp.at(2).iter().count() + self.uncles_rlp().iter().count() } /// Return List of transactions in given block. pub fn uncle_views(&self) -> Vec> { - self.rlp.at(2).iter().map(HeaderView::new_from_rlp).collect() + self.uncles_rlp().iter().map(HeaderView::new).collect() } /// Return list of uncle hashes of given block. pub fn uncle_hashes(&self) -> Vec { - self.rlp.at(2).iter().map(|rlp| keccak(rlp.as_raw())).collect() + self.uncles_rlp().iter().map(|rlp| keccak(rlp.as_raw())).collect() } /// Return nth uncle. pub fn uncle_at(&self, index: usize) -> Option
{ - self.rlp.at(2).iter().nth(index).map(|rlp| rlp.as_val()) + self.uncles_rlp().iter().nth(index).map(|rlp| rlp.as_val()) } /// Return nth uncle rlp. pub fn uncle_rlp_at(&self, index: usize) -> Option { - self.rlp.at(2).iter().nth(index).map(|rlp| rlp.as_raw().to_vec()) + self.uncles_rlp().iter().nth(index).map(|rlp| rlp.as_raw().to_vec()) } } @@ -166,7 +184,7 @@ mod tests { // that's rlp of block created with ethash engine. let rlp = "f90261f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d84568e932a80a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23f862f86002018304cb2f94ec0e71ad0a90ffe1909d27dac207f7680abba42d01801ba03a347e72953c860f32b1eb2c78a680d8734b2ea08085d949d729479796f218d5a047ea6239d9e31ccac8af3366f5ca37184d26e7646e3191a3aeb81c4cf74de500c0".from_hex().unwrap(); - let view = BlockView::new(&rlp); + let view = view!(BlockView, &rlp); assert_eq!(view.hash(), "2c9747e804293bd3f1a986484343f23bc88fd5be75dfe9d5c2860aff61e6f259".into()); assert_eq!(view.transactions_count(), 1); assert_eq!(view.uncles_count(), 0); diff --git a/ethcore/src/views/body.rs b/ethcore/src/views/body.rs index 433f7c2b68fd2a124f4134e601d529d59fdac030..6560140cad1e0a635c695c2304fd8fdfbf47e6cf 100644 --- a/ethcore/src/views/body.rs +++ b/ethcore/src/views/body.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -20,32 +20,40 @@ use bytes::Bytes; use ethereum_types::H256; use hash::keccak; use header::{Header, BlockNumber}; -use rlp::Rlp; use transaction::{LocalizedTransaction, UnverifiedTransaction}; use views::{TransactionView, HeaderView}; +use super::ViewRlp; /// View onto block rlp. pub struct BodyView<'a> { - rlp: Rlp<'a> + rlp: ViewRlp<'a> } impl<'a> BodyView<'a> { - /// Creates new view onto block from raw bytes. - pub fn new(bytes: &'a [u8]) -> BodyView<'a> { - BodyView { - rlp: Rlp::new(bytes) - } - } - - /// Creates new view onto block from rlp. - pub fn new_from_rlp(rlp: Rlp<'a>) -> BodyView<'a> { + /// Creates new view onto block body from rlp. + /// Use the `view!` macro to create this view in order to capture debugging info. + /// + /// # Example + /// + /// ``` + /// #[macro_use] + /// extern crate ethcore; + /// + /// use ethcore::views::{BodyView}; + /// + /// fn main() { + /// let bytes : &[u8] = &[]; + /// let body_view = view!(BodyView, bytes); + /// } + /// ``` + pub fn new(rlp: ViewRlp<'a>) -> BodyView<'a> { BodyView { rlp: rlp } } /// Return reference to underlaying rlp. - pub fn rlp(&self) -> &Rlp<'a> { + pub fn rlp(&self) -> &ViewRlp<'a> { &self.rlp } @@ -68,24 +76,28 @@ impl<'a> BodyView<'a> { }).collect() } + /// Return the raw rlp for the transactions in the given block. + pub fn transactions_rlp(&self) -> ViewRlp<'a> { + self.rlp.at(0) + } + /// Return number of transactions in given block, without deserializing them. pub fn transactions_count(&self) -> usize { - self.rlp.at(0).item_count() + self.transactions_rlp().item_count() } - /// Return List of transactions in given block. pub fn transaction_views(&self) -> Vec> { - self.rlp.at(0).iter().map(TransactionView::new_from_rlp).collect() + self.transactions_rlp().iter().map(TransactionView::new).collect() } /// Return transaction hashes. pub fn transaction_hashes(&self) -> Vec { - self.rlp.at(0).iter().map(|rlp| keccak(rlp.as_raw())).collect() + self.transactions_rlp().iter().map(|rlp| keccak(rlp.as_raw())).collect() } /// Returns transaction at given index without deserializing unnecessary data. pub fn transaction_at(&self, index: usize) -> Option { - self.rlp.at(0).iter().nth(index).map(|rlp| rlp.as_val()) + self.transactions_rlp().iter().nth(index).map(|rlp| rlp.as_val()) } /// Returns localized transaction at given index. @@ -99,6 +111,11 @@ impl<'a> BodyView<'a> { }) } + /// Returns raw rlp for the uncles in the given block + pub fn uncles_rlp(&self) -> ViewRlp<'a> { + self.rlp.at(1) + } + /// Return list of uncles of given block. pub fn uncles(&self) -> Vec
{ self.rlp.list_at(1) @@ -106,27 +123,27 @@ impl<'a> BodyView<'a> { /// Return number of uncles in given block, without deserializing them. pub fn uncles_count(&self) -> usize { - self.rlp.at(1).item_count() + self.uncles_rlp().item_count() } /// Return List of transactions in given block. pub fn uncle_views(&self) -> Vec> { - self.rlp.at(1).iter().map(HeaderView::new_from_rlp).collect() + self.uncles_rlp().iter().map(HeaderView::new).collect() } /// Return list of uncle hashes of given block. pub fn uncle_hashes(&self) -> Vec { - self.rlp.at(1).iter().map(|rlp| keccak(rlp.as_raw())).collect() + self.uncles_rlp().iter().map(|rlp| keccak(rlp.as_raw())).collect() } /// Return nth uncle. pub fn uncle_at(&self, index: usize) -> Option
{ - self.rlp.at(1).iter().nth(index).map(|rlp| rlp.as_val()) + self.uncles_rlp().iter().nth(index).map(|rlp| rlp.as_val()) } /// Return nth uncle rlp. pub fn uncle_rlp_at(&self, index: usize) -> Option { - self.rlp.at(1).iter().nth(index).map(|rlp| rlp.as_raw().to_vec()) + self.uncles_rlp().iter().nth(index).map(|rlp| rlp.as_raw().to_vec()) } } @@ -141,7 +158,7 @@ mod tests { // that's rlp of block created with ethash engine. let rlp = "f90261f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d84568e932a80a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23f862f86002018304cb2f94ec0e71ad0a90ffe1909d27dac207f7680abba42d01801ba03a347e72953c860f32b1eb2c78a680d8734b2ea08085d949d729479796f218d5a047ea6239d9e31ccac8af3366f5ca37184d26e7646e3191a3aeb81c4cf74de500c0".from_hex().unwrap(); let body = BlockChain::block_to_body(&rlp); - let view = BodyView::new(&body); + let view = view!(BodyView, &body); assert_eq!(view.transactions_count(), 1); assert_eq!(view.uncles_count(), 0); } diff --git a/ethcore/src/views/header.rs b/ethcore/src/views/header.rs index cce4eee82814620a931f8c986d2b047342cd599f..4b7b1225d0213981645b2b264372c44ab41d7459 100644 --- a/ethcore/src/views/header.rs +++ b/ethcore/src/views/header.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -20,35 +20,44 @@ use bytes::Bytes; use ethereum_types::{H256, Bloom, U256, Address}; use hash::keccak; use header::BlockNumber; -use rlp::{self, Rlp}; +use rlp::{self}; +use super::ViewRlp; /// View onto block header rlp. pub struct HeaderView<'a> { - rlp: Rlp<'a> + rlp: ViewRlp<'a> } impl<'a> HeaderView<'a> { - /// Creates new view onto header from raw bytes. - pub fn new(bytes: &'a [u8]) -> HeaderView<'a> { + /// Creates a new Header view from valid ViewRlp + /// Use the `view!` macro to create this view in order to capture debugging info. + /// + /// # Example + /// + /// ``` + /// #[macro_use] + /// extern crate ethcore; + /// + /// use ethcore::views::{HeaderView}; + /// + /// fn main() { + /// let bytes : &[u8] = &[]; + /// let tx_view = view!(HeaderView, bytes); + /// } + /// ``` + pub fn new(rlp: ViewRlp<'a>) -> HeaderView<'a> { HeaderView { - rlp: Rlp::new(bytes) - } - } - - /// Creates new view onto header from rlp. - pub fn new_from_rlp(rlp: Rlp<'a>) -> HeaderView<'a> { - HeaderView { - rlp: rlp + rlp } } /// Returns header hash. pub fn hash(&self) -> H256 { - keccak(self.rlp.as_raw()) + keccak(self.rlp.rlp.as_raw()) } /// Returns raw rlp. - pub fn rlp(&self) -> &Rlp<'a> { &self.rlp } + pub fn rlp(&self) -> &ViewRlp<'a> { &self.rlp } /// Returns parent hash. pub fn parent_hash(&self) -> H256 { self.rlp.val_at(0) } @@ -102,9 +111,10 @@ impl<'a> HeaderView<'a> { pub fn decode_seal(&self) -> Result, rlp::DecoderError> { let seal = self.seal(); seal.into_iter() - .map(|s| rlp::UntrustedRlp::new(&s).data().map(|x| x.to_vec())) + .map(|s| rlp::Rlp::new(&s).data().map(|x| x.to_vec())) .collect() } + } #[cfg(test)] @@ -120,7 +130,7 @@ mod tests { let mix_hash = "a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd".from_hex().unwrap(); let nonce = "88ab4e252a7e8c2a23".from_hex().unwrap(); - let view = HeaderView::new(&rlp); + let view = view!(HeaderView, &rlp); assert_eq!(view.hash(), "2c9747e804293bd3f1a986484343f23bc88fd5be75dfe9d5c2860aff61e6f259".into()); assert_eq!(view.parent_hash(), "d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7".into()); assert_eq!(view.uncles_hash(), "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347".into()); diff --git a/ethcore/src/views/mod.rs b/ethcore/src/views/mod.rs index 5d3cc8cdccd78fa398272c768feb3197b8807d3a..6d326493825552ff6605de7fc0ed9859ae366782 100644 --- a/ethcore/src/views/mod.rs +++ b/ethcore/src/views/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -16,12 +16,26 @@ //! Block oriented views onto rlp. +#[macro_use] +mod view_rlp; mod block; mod body; mod header; mod transaction; +pub use self::view_rlp::ViewRlp; pub use self::block::BlockView; pub use self::body::BodyView; pub use self::header::HeaderView; pub use self::transaction::TransactionView; + +#[cfg(test)] +mod tests { + use super::HeaderView; + + #[test] + #[should_panic(expected="View rlp is trusted and should be valid. Constructed in ethcore/src/views/mod.rs on line 39: RlpExpectedToBeList")] + fn should_include_file_line_number_in_panic_for_invalid_rlp() { + let _ = view!(HeaderView, &[]).parent_hash(); + } +} diff --git a/ethcore/src/views/transaction.rs b/ethcore/src/views/transaction.rs index 92bd49c276835d33776ea6db85a798997ba24238..911fde944e3f7292cae820bee69ead9d8734a5c6 100644 --- a/ethcore/src/views/transaction.rs +++ b/ethcore/src/views/transaction.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -18,30 +18,39 @@ use bytes::Bytes; use ethereum_types::{H256, U256}; use hash::keccak; -use rlp::Rlp; +// use rlp::{Rlp, Decodable}; +use super::ViewRlp; /// View onto transaction rlp. pub struct TransactionView<'a> { - rlp: Rlp<'a> + rlp: ViewRlp<'a> } impl<'a> TransactionView<'a> { - /// Creates new view onto block from raw bytes. - pub fn new(bytes: &'a [u8]) -> TransactionView<'a> { - TransactionView { - rlp: Rlp::new(bytes) - } - } - - /// Creates new view onto block from rlp. - pub fn new_from_rlp(rlp: Rlp<'a>) -> TransactionView<'a> { + /// Creates new view onto valid transaction rlp. + /// Use the `view!` macro to create this view in order to capture debugging info. + /// + /// # Example + /// + /// ``` + /// #[macro_use] + /// extern crate ethcore; + /// + /// use ethcore::views::{TransactionView}; + /// + /// fn main() { + /// let bytes : &[u8] = &[]; + /// let tx_view = view!(TransactionView, bytes); + /// } + /// ``` + pub fn new(rlp: ViewRlp<'a>) -> TransactionView<'a> { TransactionView { rlp: rlp } } /// Return reference to underlaying rlp. - pub fn rlp(&self) -> &Rlp<'a> { + pub fn rlp(&self) -> &ViewRlp<'a> { &self.rlp } @@ -84,7 +93,7 @@ mod tests { fn test_transaction_view() { let rlp = "f87c80018261a894095e7baea6a6c7c4c2dfeb977efac326af552d870a9d00000000000000000000000000000000000000000000000000000000001ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804".from_hex().unwrap(); - let view = TransactionView::new(&rlp); + let view = view!(TransactionView, &rlp); assert_eq!(view.nonce(), 0.into()); assert_eq!(view.gas_price(), 1.into()); assert_eq!(view.gas(), 0x61a8.into()); diff --git a/ethcore/src/views/view_rlp.rs b/ethcore/src/views/view_rlp.rs new file mode 100644 index 0000000000000000000000000000000000000000..2dd1b33a946915b017d066dae46eb7705351e90e --- /dev/null +++ b/ethcore/src/views/view_rlp.rs @@ -0,0 +1,135 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Wrapper for view rlp expected to be valid with debug info + +use rlp::{Rlp, Decodable, DecoderError}; + +/// Wrapper for trusted rlp, which is expected to be valid, for use in views +/// When created with view!, records the file and line where it was created for debugging +pub struct ViewRlp<'a> { + /// Wrapped Rlp, expected to be valid + pub rlp: Rlp<'a>, + file: &'a str, + line: u32, +} + +impl<'a, 'view> ViewRlp<'a> where 'a : 'view { + #[doc(hidden)] + pub fn new(bytes: &'a [u8], file: &'a str, line: u32) -> Self { + ViewRlp { + rlp: Rlp::new(bytes), + file, + line + } + } + + /// Returns a new instance replacing existing rlp with new rlp, maintaining debug info + fn new_from_rlp(&self, rlp: Rlp<'a>) -> Self { + ViewRlp { + rlp, + file: self.file, + line: self.line + } + } + + fn maybe_at(&self, index: usize) -> Option> { + self.rlp.at(index) + .map(|rlp| self.new_from_rlp(rlp)) + .ok() + } + + fn expect_valid_rlp(&self, r: Result) -> T { + r.unwrap_or_else(|e| panic!( + "View rlp is trusted and should be valid. Constructed in {} on line {}: {}", + self.file, + self.line, + e + )) + } + + /// Returns rlp at the given index, panics if no rlp at that index + pub fn at(&self, index: usize) -> ViewRlp<'a> { + let rlp = self.expect_valid_rlp(self.rlp.at(index)); + self.new_from_rlp(rlp) + } + + /// Returns an iterator over all rlp values + pub fn iter(&'view self) -> ViewRlpIterator<'a, 'view> { + self.into_iter() + } + + /// Returns decoded value of this rlp, panics if rlp not valid + pub fn as_val(&self) -> T where T: Decodable { + self.expect_valid_rlp(self.rlp.as_val()) + } + + /// Returns decoded value at the given index, panics not present or valid at that index + pub fn val_at(&self, index: usize) -> T where T : Decodable { + self.expect_valid_rlp(self.rlp.val_at(index)) + } + + /// Returns decoded list of values, panics if rlp is invalid + pub fn list_at(&self, index: usize) -> Vec where T: Decodable { + self.expect_valid_rlp(self.rlp.list_at(index)) + } + + /// Returns the number of items in the rlp, panics if it is not a list of rlp values + pub fn item_count(&self) -> usize { + self.expect_valid_rlp(self.rlp.item_count()) + } + + /// Returns raw rlp bytes + pub fn as_raw(&'view self) -> &'a [u8] { + self.rlp.as_raw() + } +} + +/// Iterator over rlp-slice list elements. +pub struct ViewRlpIterator<'a, 'view> where 'a: 'view { + rlp: &'view ViewRlp<'a>, + index: usize, +} + +impl<'a, 'view> IntoIterator for &'view ViewRlp<'a> where 'a: 'view { + type Item = ViewRlp<'a>; + type IntoIter = ViewRlpIterator<'a, 'view>; + + fn into_iter(self) -> Self::IntoIter { + ViewRlpIterator { + rlp: self, + index: 0, + } + } +} + +impl<'a, 'view> Iterator for ViewRlpIterator<'a, 'view> { + type Item = ViewRlp<'a>; + + fn next(&mut self) -> Option> { + let index = self.index; + let result = self.rlp.maybe_at(index); + self.index += 1; + result + } +} + +#[macro_export] +macro_rules! view { + ($view: ident, $bytes: expr) => { + $view::new($crate::views::ViewRlp::new($bytes, file!(), line!())) + }; +} diff --git a/stratum/Cargo.toml b/ethcore/stratum/Cargo.toml similarity index 77% rename from stratum/Cargo.toml rename to ethcore/stratum/Cargo.toml index 0faed618638ca516bf2a030796b14bab3773d844..bf967df504aef39d351064db9b2c1517fb874d40 100644 --- a/stratum/Cargo.toml +++ b/ethcore/stratum/Cargo.toml @@ -1,14 +1,13 @@ [package] description = "Ethcore stratum lib" name = "ethcore-stratum" -version = "1.11.0" +version = "1.12.0" license = "GPL-3.0" authors = ["Parity Technologies "] [dependencies] -ethcore-logger = { path = "../logger" } -ethereum-types = "0.2" -keccak-hash = { path = "../util/hash" } +ethereum-types = "0.3" +keccak-hash = { path = "../../util/hash" } jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.11" } jsonrpc-macros = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.11" } jsonrpc-tcp-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.11" } @@ -19,4 +18,4 @@ parking_lot = "0.5" env_logger = "0.4" tokio-core = "0.1" tokio-io = "0.1" -ethcore-logger = { path = "../logger" } +ethcore-logger = { path = "../../logger" } diff --git a/stratum/src/lib.rs b/ethcore/stratum/src/lib.rs similarity index 96% rename from stratum/src/lib.rs rename to ethcore/stratum/src/lib.rs index d43bcc5569e0da04a871b57a787e97565880f3a9..0e9de9b43c54928dcd1104394c596e836b0eaece 100644 --- a/stratum/src/lib.rs +++ b/ethcore/stratum/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -77,10 +77,10 @@ impl Stratum { ) -> Result, Error> { let implementation = Arc::new(StratumImpl { - subscribers: RwLock::new(Vec::new()), - job_que: RwLock::new(HashSet::new()), + subscribers: RwLock::default(), + job_que: RwLock::default(), dispatcher, - workers: Arc::new(RwLock::new(HashMap::new())), + workers: Arc::new(RwLock::default()), secret, notify_counter: RwLock::new(NOTIFY_COUNTER_INITIAL), }); @@ -323,7 +323,6 @@ impl MetaExtractor for PeerMetaExtractor { #[cfg(test)] mod tests { use super::*; - use std::str::FromStr; use std::net::SocketAddr; use std::sync::Arc; @@ -366,7 +365,7 @@ mod tests { #[test] fn can_be_started() { - let stratum = Stratum::start(&SocketAddr::from_str("127.0.0.1:19980").unwrap(), Arc::new(VoidManager), None); + let stratum = Stratum::start(&"127.0.0.1:19980".parse().unwrap(), Arc::new(VoidManager), None); assert!(stratum.is_ok()); } @@ -374,7 +373,7 @@ mod tests { fn records_subscriber() { init_log(); - let addr = SocketAddr::from_str("127.0.0.1:19985").unwrap(); + let addr = "127.0.0.1:19985".parse().unwrap(); let stratum = Stratum::start(&addr, Arc::new(VoidManager), None).unwrap(); let request = r#"{"jsonrpc": "2.0", "method": "mining.subscribe", "params": [], "id": 1}"#; dummy_request(&addr, request); @@ -419,7 +418,7 @@ mod tests { #[test] fn receives_initial_paylaod() { - let addr = SocketAddr::from_str("127.0.0.1:19975").unwrap(); + let addr = "127.0.0.1:19975".parse().unwrap(); let _stratum = Stratum::start(&addr, DummyManager::new(), None).expect("There should be no error starting stratum"); let request = r#"{"jsonrpc": "2.0", "method": "mining.subscribe", "params": [], "id": 2}"#; @@ -430,7 +429,7 @@ mod tests { #[test] fn can_authorize() { - let addr = SocketAddr::from_str("127.0.0.1:19970").unwrap(); + let addr = "127.0.0.1:19970".parse().unwrap(); let stratum = Stratum::start( &addr, Arc::new(DummyManager::build().of_initial(r#"["dummy autorize payload"]"#)), @@ -448,7 +447,7 @@ mod tests { fn can_push_work() { init_log(); - let addr = SocketAddr::from_str("127.0.0.1:19995").unwrap(); + let addr = "127.0.0.1:19995".parse().unwrap(); let stratum = Stratum::start( &addr, Arc::new(DummyManager::build().of_initial(r#"["dummy autorize payload"]"#)), diff --git a/stratum/src/traits.rs b/ethcore/stratum/src/traits.rs similarity index 97% rename from stratum/src/traits.rs rename to ethcore/stratum/src/traits.rs index 431d338a428f15dd9a108fc8bc2a5b0e2e40e30f..d1bb9a4da731b14aa1f37037d0a0027c28d33897 100644 --- a/stratum/src/traits.rs +++ b/ethcore/stratum/src/traits.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/sync/Cargo.toml b/ethcore/sync/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..66ee5662158749fe0744dc7aea4beb767659b5da --- /dev/null +++ b/ethcore/sync/Cargo.toml @@ -0,0 +1,41 @@ +[package] +description = "Ethcore blockchain sync" +name = "ethcore-sync" +version = "1.12.0" +license = "GPL-3.0" +authors = ["Parity Technologies "] + +[lib] + +[dependencies] +ethcore-bytes = { path = "../../util/bytes" } +ethcore-network = { path = "../../util/network" } +ethcore-network-devp2p = { path = "../../util/network-devp2p" } +ethcore-io = { path = "../../util/io" } +ethcore-light = { path = "../light" } +ethcore-transaction = { path = "../transaction" } +ethcore = { path = ".." } +ethereum-types = "0.3" +plain_hasher = { path = "../../util/plain_hasher" } +rlp = { path = "../../util/rlp" } +rustc-hex = "1.0" +keccak-hash = { path = "../../util/hash" } +triehash = { path = "../../util/triehash" } +kvdb = { path = "../../util/kvdb" } +macros = { path = "../../util/macros" } +log = "0.3" +env_logger = "0.4" +rand = "0.4" +heapsize = "0.4" +semver = "0.9" +smallvec = { version = "0.4", features = ["heapsizeof"] } +parking_lot = "0.5" +trace-time = { path = "../../util/trace-time" } +ipnetwork = "0.12.6" + +[dev-dependencies] +ethcore-io = { path = "../../util/io", features = ["mio"] } +ethkey = { path = "../../ethkey" } +kvdb-memorydb = { path = "../../util/kvdb-memorydb" } +ethcore-private-tx = { path = "../private-tx" } +ethcore = { path = "..", features = ["test-helpers"] } diff --git a/sync/src/api.rs b/ethcore/sync/src/api.rs similarity index 81% rename from sync/src/api.rs rename to ethcore/sync/src/api.rs index 38543de15c3cd28add4be966010334825d69c6a5..56bc579ad886697139b6892d529a649a9ed6f7f0 100644 --- a/sync/src/api.rs +++ b/ethcore/sync/src/api.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -17,14 +17,17 @@ use std::sync::Arc; use std::collections::{HashMap, BTreeMap}; use std::io; +use std::ops::Range; +use std::time::Duration; use bytes::Bytes; -use devp2p::{NetworkService, ConnectionFilter}; -use network::{NetworkProtocolHandler, NetworkContext, HostInfo, PeerId, ProtocolId, - NetworkConfiguration as BasicNetworkConfiguration, NonReservedPeerMode, Error, ErrorKind}; +use devp2p::NetworkService; +use network::{NetworkProtocolHandler, NetworkContext, PeerId, ProtocolId, + NetworkConfiguration as BasicNetworkConfiguration, NonReservedPeerMode, Error, ErrorKind, + ConnectionFilter}; use ethereum_types::{H256, H512, U256}; use io::{TimerToken}; use ethcore::ethstore::ethkey::Secret; -use ethcore::client::{BlockChainClient, ChainNotify}; +use ethcore::client::{BlockChainClient, ChainNotify, ChainRoute, ChainMessageType}; use ethcore::snapshot::SnapshotService; use ethcore::header::BlockNumber; use sync_io::NetSyncIo; @@ -32,11 +35,14 @@ use chain::{ChainSync, SyncStatus as EthSyncStatus}; use std::net::{SocketAddr, AddrParseError}; use std::str::FromStr; use parking_lot::RwLock; -use chain::{ETH_PACKET_COUNT, SNAPSHOT_SYNC_PACKET_COUNT}; +use chain::{ETH_PROTOCOL_VERSION_63, ETH_PROTOCOL_VERSION_62, + PAR_PROTOCOL_VERSION_1, PAR_PROTOCOL_VERSION_2, PAR_PROTOCOL_VERSION_3}; use light::client::AsLightClient; use light::Provider; use light::net::{self as light_net, LightProtocol, Params as LightParams, Capabilities, Handler as LightHandler, EventContext}; use network::IpFilter; +use private_tx::PrivateTxHandler; +use transaction::UnverifiedTransaction; /// Parity sync protocol pub const WARP_SYNC_PROTOCOL_ID: ProtocolId = *b"par"; @@ -45,6 +51,41 @@ pub const ETH_PROTOCOL: ProtocolId = *b"eth"; /// Ethereum light protocol pub const LIGHT_PROTOCOL: ProtocolId = *b"pip"; +/// Determine warp sync status. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum WarpSync { + /// Warp sync is enabled. + Enabled, + /// Warp sync is disabled. + Disabled, + /// Only warp sync is allowed (no regular sync) and only after given block number. + OnlyAndAfter(BlockNumber), +} + +impl WarpSync { + /// Returns true if warp sync is enabled. + pub fn is_enabled(&self) -> bool { + match *self { + WarpSync::Enabled => true, + WarpSync::OnlyAndAfter(_) => true, + WarpSync::Disabled => false, + } + } + + /// Returns `true` if we are in warp-only mode. + /// + /// i.e. we will never fall back to regular sync + /// until given block number is reached by + /// successfuly finding and restoring from a snapshot. + pub fn is_warp_only(&self) -> bool { + if let WarpSync::OnlyAndAfter(_) = *self { + true + } else { + false + } + } +} + /// Sync configuration #[derive(Debug, Clone, Copy)] pub struct SyncConfig { @@ -61,7 +102,7 @@ pub struct SyncConfig { /// Fork block to check pub fork_block: Option<(BlockNumber, H256)>, /// Enable snapshot sync - pub warp_sync: bool, + pub warp_sync: WarpSync, /// Enable light client server. pub serve_light: bool, } @@ -75,7 +116,7 @@ impl Default for SyncConfig { subprotocol_name: ETH_PROTOCOL, light_subprotocol_name: LIGHT_PROTOCOL, fork_block: None, - warp_sync: false, + warp_sync: WarpSync::Disabled, serve_light: false, } } @@ -163,10 +204,8 @@ pub struct AttachedProtocol { pub handler: Arc, /// 3-character ID for the protocol. pub protocol_id: ProtocolId, - /// Packet count. - pub packet_count: u8, - /// Supported versions. - pub versions: &'static [u8], + /// Supported versions and their packet counts. + pub versions: &'static [(u8, u8)], } impl AttachedProtocol { @@ -174,7 +213,6 @@ impl AttachedProtocol { let res = network.register_protocol( self.handler.clone(), self.protocol_id, - self.packet_count, self.versions ); @@ -192,6 +230,8 @@ pub struct Params { pub chain: Arc, /// Snapshot service. pub snapshot_service: Arc, + /// Private tx service. + pub private_tx_handler: Arc, /// Light data provider. pub provider: Arc<::light::Provider>, /// Network layer configuration. @@ -253,7 +293,7 @@ impl EthSync { }) }; - let chain_sync = ChainSync::new(params.config, &*params.chain); + let chain_sync = ChainSync::new(params.config, &*params.chain, params.private_tx_handler.clone()); let service = NetworkService::new(params.network_config.clone().into_basic()?, connection_filter)?; let sync = Arc::new(EthSync { @@ -277,7 +317,7 @@ impl EthSync { impl SyncProvider for EthSync { /// Get sync status fn status(&self) -> EthSyncStatus { - self.eth_handler.sync.write().status() + self.eth_handler.sync.read().status() } /// Get sync peers @@ -319,6 +359,10 @@ impl SyncProvider for EthSync { } } +const PEERS_TIMER: TimerToken = 0; +const SYNC_TIMER: TimerToken = 1; +const TX_TIMER: TimerToken = 2; + struct SyncProtocolHandler { /// Shared blockchain client. chain: Arc, @@ -331,17 +375,21 @@ struct SyncProtocolHandler { } impl NetworkProtocolHandler for SyncProtocolHandler { - fn initialize(&self, io: &NetworkContext, _host_info: &HostInfo) { + fn initialize(&self, io: &NetworkContext) { if io.subprotocol_name() != WARP_SYNC_PROTOCOL_ID { - io.register_timer(0, 1000).expect("Error registering sync timer"); + io.register_timer(PEERS_TIMER, Duration::from_millis(700)).expect("Error registering peers timer"); + io.register_timer(SYNC_TIMER, Duration::from_millis(1100)).expect("Error registering sync timer"); + io.register_timer(TX_TIMER, Duration::from_millis(1300)).expect("Error registering transactions timer"); } } fn read(&self, io: &NetworkContext, peer: &PeerId, packet_id: u8, data: &[u8]) { + trace_time!("sync::read"); ChainSync::dispatch_packet(&self.sync, &mut NetSyncIo::new(io, &*self.chain, &*self.snapshot_service, &self.overlay), *peer, packet_id, data); } fn connected(&self, io: &NetworkContext, peer: &PeerId) { + trace_time!("sync::connected"); // If warp protocol is supported only allow warp handshake let warp_protocol = io.protocol_version(WARP_SYNC_PROTOCOL_ID, *peer).unwrap_or(0) != 0; let warp_context = io.subprotocol_name() == WARP_SYNC_PROTOCOL_ID; @@ -351,15 +399,23 @@ impl NetworkProtocolHandler for SyncProtocolHandler { } fn disconnected(&self, io: &NetworkContext, peer: &PeerId) { + trace_time!("sync::disconnected"); if io.subprotocol_name() != WARP_SYNC_PROTOCOL_ID { self.sync.write().on_peer_aborting(&mut NetSyncIo::new(io, &*self.chain, &*self.snapshot_service, &self.overlay), *peer); } } - fn timeout(&self, io: &NetworkContext, _timer: TimerToken) { - self.sync.write().maintain_peers(&mut NetSyncIo::new(io, &*self.chain, &*self.snapshot_service, &self.overlay)); - self.sync.write().maintain_sync(&mut NetSyncIo::new(io, &*self.chain, &*self.snapshot_service, &self.overlay)); - self.sync.write().propagate_new_transactions(&mut NetSyncIo::new(io, &*self.chain, &*self.snapshot_service, &self.overlay)); + fn timeout(&self, io: &NetworkContext, timer: TimerToken) { + trace_time!("sync::timeout"); + let mut io = NetSyncIo::new(io, &*self.chain, &*self.snapshot_service, &self.overlay); + match timer { + PEERS_TIMER => self.sync.write().maintain_peers(&mut io), + SYNC_TIMER => self.sync.write().maintain_sync(&mut io), + TX_TIMER => { + self.sync.write().propagate_new_transactions(&mut io); + }, + _ => warn!("Unknown timer {} triggered.", timer), + } } } @@ -367,22 +423,22 @@ impl ChainNotify for EthSync { fn new_blocks(&self, imported: Vec, invalid: Vec, - enacted: Vec, - retracted: Vec, + route: ChainRoute, sealed: Vec, proposed: Vec, - _duration: u64) + _duration: Duration) { use light::net::Announcement; self.network.with_context(self.subprotocol_name, |context| { - let mut sync_io = NetSyncIo::new(context, &*self.eth_handler.chain, &*self.eth_handler.snapshot_service, &self.eth_handler.overlay); + let mut sync_io = NetSyncIo::new(context, &*self.eth_handler.chain, &*self.eth_handler.snapshot_service, + &self.eth_handler.overlay); self.eth_handler.sync.write().chain_new_blocks( &mut sync_io, &imported, &invalid, - &enacted, - &retracted, + route.enacted(), + route.retracted(), &sealed, &proposed); }); @@ -408,20 +464,27 @@ impl ChainNotify for EthSync { } fn start(&self) { - match self.network.start().map_err(Into::into) { - Err(ErrorKind::Io(ref e)) if e.kind() == io::ErrorKind::AddrInUse => warn!("Network port {:?} is already in use, make sure that another instance of an Ethereum client is not running or change the port using the --port option.", self.network.config().listen_address.expect("Listen address is not set.")), - Err(err) => warn!("Error starting network: {}", err), + match self.network.start() { + Err((err, listen_address)) => { + match err.into() { + ErrorKind::Io(ref e) if e.kind() == io::ErrorKind::AddrInUse => { + warn!("Network port {:?} is already in use, make sure that another instance of an Ethereum client is not running or change the port using the --port option.", listen_address.expect("Listen address is not set.")) + }, + err => warn!("Error starting network: {}", err), + } + }, _ => {}, } - self.network.register_protocol(self.eth_handler.clone(), self.subprotocol_name, ETH_PACKET_COUNT, &[62u8, 63u8]) + + self.network.register_protocol(self.eth_handler.clone(), self.subprotocol_name, &[ETH_PROTOCOL_VERSION_62, ETH_PROTOCOL_VERSION_63]) .unwrap_or_else(|e| warn!("Error registering ethereum protocol: {:?}", e)); // register the warp sync subprotocol - self.network.register_protocol(self.eth_handler.clone(), WARP_SYNC_PROTOCOL_ID, SNAPSHOT_SYNC_PACKET_COUNT, &[1u8, 2u8]) + self.network.register_protocol(self.eth_handler.clone(), WARP_SYNC_PROTOCOL_ID, &[PAR_PROTOCOL_VERSION_1, PAR_PROTOCOL_VERSION_2, PAR_PROTOCOL_VERSION_3]) .unwrap_or_else(|e| warn!("Error registering snapshot sync protocol: {:?}", e)); // register the light protocol. if let Some(light_proto) = self.light_proto.as_ref().map(|x| x.clone()) { - self.network.register_protocol(light_proto, self.light_subprotocol_name, ::light::net::PACKET_COUNT, ::light::net::PROTOCOL_VERSIONS) + self.network.register_protocol(light_proto, self.light_subprotocol_name, ::light::net::PROTOCOL_VERSIONS) .unwrap_or_else(|e| warn!("Error registering light client protocol: {:?}", e)); } @@ -431,19 +494,23 @@ impl ChainNotify for EthSync { fn stop(&self) { self.eth_handler.snapshot_service.abort_restore(); - self.network.stop().unwrap_or_else(|e| warn!("Error stopping network: {:?}", e)); + self.network.stop(); } - fn broadcast(&self, message: Vec) { + fn broadcast(&self, message_type: ChainMessageType) { self.network.with_context(WARP_SYNC_PROTOCOL_ID, |context| { let mut sync_io = NetSyncIo::new(context, &*self.eth_handler.chain, &*self.eth_handler.snapshot_service, &self.eth_handler.overlay); - self.eth_handler.sync.write().propagate_consensus_packet(&mut sync_io, message.clone()); + match message_type { + ChainMessageType::Consensus(message) => self.eth_handler.sync.write().propagate_consensus_packet(&mut sync_io, message), + ChainMessageType::PrivateTransaction(message) => self.eth_handler.sync.write().propagate_private_transaction(&mut sync_io, message), + ChainMessageType::SignedPrivateTransaction(message) => self.eth_handler.sync.write().propagate_signed_private_transaction(&mut sync_io, message), + } }); } - fn transactions_received(&self, hashes: Vec, peer_id: PeerId) { + fn transactions_received(&self, txs: &[UnverifiedTransaction], peer_id: PeerId) { let mut sync = self.eth_handler.sync.write(); - sync.transactions_received(hashes, peer_id); + sync.transactions_received(txs, peer_id); } } @@ -472,13 +539,14 @@ pub trait ManageNetwork : Send + Sync { fn start_network(&self); /// Stop network fn stop_network(&self); - /// Query the current configuration of the network - fn network_config(&self) -> NetworkConfiguration; + /// Returns the minimum and maximum peers. + /// Note that `range.end` is *exclusive*. + // TODO: Range should be changed to RangeInclusive once stable (https://github.com/rust-lang/rust/pull/50758) + fn num_peers_range(&self) -> Range; /// Get network context for protocol. fn with_proto_context(&self, proto: ProtocolId, f: &mut FnMut(&NetworkContext)); } - impl ManageNetwork for EthSync { fn accept_unreserved_peers(&self) { self.network.set_non_reserved_mode(NonReservedPeerMode::Accept); @@ -513,8 +581,8 @@ impl ManageNetwork for EthSync { self.stop(); } - fn network_config(&self) -> NetworkConfiguration { - NetworkConfiguration::from(self.network.config().clone()) + fn num_peers_range(&self) -> Range { + self.network.num_peers_range() } fn with_proto_context(&self, proto: ProtocolId, f: &mut FnMut(&NetworkContext)) { @@ -578,7 +646,7 @@ impl NetworkConfiguration { config_path: self.config_path, net_config_path: self.net_config_path, listen_address: match self.listen_address { None => None, Some(addr) => Some(SocketAddr::from_str(&addr)?) }, - public_address: match self.public_address { None => None, Some(addr) => Some(SocketAddr::from_str(&addr)?) }, + public_address: match self.public_address { None => None, Some(addr) => Some(SocketAddr::from_str(&addr)?) }, udp_port: self.udp_port, nat_enabled: self.nat_enabled, discovery_enabled: self.discovery_enabled, @@ -767,17 +835,21 @@ impl ManageNetwork for LightSync { } fn start_network(&self) { - match self.network.start().map_err(Into::into) { - Err(ErrorKind::Io(ref e)) if e.kind() == io::ErrorKind::AddrInUse => { - warn!("Network port {:?} is already in use, make sure that another instance of an Ethereum client is not running or change the port using the --port option.", self.network.config().listen_address.expect("Listen address is not set.")) - } - Err(err) => warn!("Error starting network: {}", err), + match self.network.start() { + Err((err, listen_address)) => { + match err.into() { + ErrorKind::Io(ref e) if e.kind() == io::ErrorKind::AddrInUse => { + warn!("Network port {:?} is already in use, make sure that another instance of an Ethereum client is not running or change the port using the --port option.", listen_address.expect("Listen address is not set.")) + }, + err => warn!("Error starting network: {}", err), + } + }, _ => {}, } let light_proto = self.proto.clone(); - self.network.register_protocol(light_proto, self.subprotocol_name, ::light::net::PACKET_COUNT, ::light::net::PROTOCOL_VERSIONS) + self.network.register_protocol(light_proto, self.subprotocol_name, ::light::net::PROTOCOL_VERSIONS) .unwrap_or_else(|e| warn!("Error registering light client protocol: {:?}", e)); for proto in &self.attached_protos { proto.register(&self.network) } @@ -785,13 +857,11 @@ impl ManageNetwork for LightSync { fn stop_network(&self) { self.proto.abort(); - if let Err(e) = self.network.stop() { - warn!("Error stopping network: {}", e); - } + self.network.stop(); } - fn network_config(&self) -> NetworkConfiguration { - NetworkConfiguration::from(self.network.config().clone()) + fn num_peers_range(&self) -> Range { + self.network.num_peers_range() } fn with_proto_context(&self, proto: ProtocolId, f: &mut FnMut(&NetworkContext)) { @@ -802,12 +872,13 @@ impl ManageNetwork for LightSync { impl LightSyncProvider for LightSync { fn peer_numbers(&self) -> PeerNumbers { let (connected, active) = self.proto.peer_count(); - let config = self.network_config(); + let peers_range = self.num_peers_range(); + debug_assert!(peers_range.end > peers_range.start); PeerNumbers { connected: connected, active: active, - max: config.max_peers as usize, - min: config.min_peers as usize, + max: peers_range.end as usize - 1, + min: peers_range.start as usize, } } diff --git a/sync/src/block_sync.rs b/ethcore/sync/src/block_sync.rs similarity index 93% rename from sync/src/block_sync.rs rename to ethcore/sync/src/block_sync.rs index 9e72c9d53db8f07d4a7da109102c9db958b485b6..bff9bb071ab0cd247917dbdb1fa31de15c2f9dca 100644 --- a/sync/src/block_sync.rs +++ b/ethcore/sync/src/block_sync.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -22,12 +22,12 @@ use std::collections::{HashSet, VecDeque}; use std::cmp; use heapsize::HeapSizeOf; use ethereum_types::H256; -use rlp::*; -use ethcore::views::{BlockView}; +use rlp::Rlp; +use ethcore::views::BlockView; use ethcore::header::{BlockNumber, Header as BlockHeader}; -use ethcore::client::{BlockStatus, BlockId, BlockImportError}; +use ethcore::client::{BlockStatus, BlockId, BlockImportError, BlockImportErrorKind}; use ethcore::block::Block; -use ethcore::error::{ImportError, BlockError}; +use ethcore::error::{ImportErrorKind, BlockError}; use sync_io::SyncIo; use blocks::BlockCollection; @@ -216,7 +216,7 @@ impl BlockDownloader { } /// Add new block headers. - pub fn import_headers(&mut self, io: &mut SyncIo, r: &UntrustedRlp, expected_hash: Option) -> Result { + pub fn import_headers(&mut self, io: &mut SyncIo, r: &Rlp, expected_hash: Option) -> Result { let item_count = r.item_count().unwrap_or(0); if self.state == State::Idle { trace!(target: "sync", "Ignored unexpected block headers"); @@ -316,7 +316,7 @@ impl BlockDownloader { } /// Called by peer once it has new block bodies - pub fn import_bodies(&mut self, _io: &mut SyncIo, r: &UntrustedRlp) -> Result<(), BlockDownloaderImportError> { + pub fn import_bodies(&mut self, _io: &mut SyncIo, r: &Rlp) -> Result<(), BlockDownloaderImportError> { let item_count = r.item_count().unwrap_or(0); if item_count == 0 { return Err(BlockDownloaderImportError::Useless); @@ -342,7 +342,7 @@ impl BlockDownloader { } /// Called by peer once it has new block bodies - pub fn import_receipts(&mut self, _io: &mut SyncIo, r: &UntrustedRlp) -> Result<(), BlockDownloaderImportError> { + pub fn import_receipts(&mut self, _io: &mut SyncIo, r: &Rlp) -> Result<(), BlockDownloaderImportError> { let item_count = r.item_count().unwrap_or(0); if item_count == 0 { return Err(BlockDownloaderImportError::Useless); @@ -478,7 +478,7 @@ impl BlockDownloader { let block = block_and_receipts.block; let receipts = block_and_receipts.receipts; let (h, number, parent) = { - let header = BlockView::new(&block).header_view(); + let header = view!(BlockView, &block).header_view(); (header.hash(), header.number(), header.parent_hash()) }; @@ -496,17 +496,17 @@ impl BlockDownloader { } let result = if let Some(receipts) = receipts { - io.chain().import_block_with_receipts(block, receipts) + io.chain().queue_ancient_block(block, receipts) } else { io.chain().import_block(block) }; match result { - Err(BlockImportError::Import(ImportError::AlreadyInChain)) => { + Err(BlockImportError(BlockImportErrorKind::Import(ImportErrorKind::AlreadyInChain), _)) => { trace!(target: "sync", "Block already in chain {:?}", h); self.block_imported(&h, number, &parent); }, - Err(BlockImportError::Import(ImportError::AlreadyQueued)) => { + Err(BlockImportError(BlockImportErrorKind::Import(ImportErrorKind::AlreadyQueued), _)) => { trace!(target: "sync", "Block already queued {:?}", h); self.block_imported(&h, number, &parent); }, @@ -515,14 +515,14 @@ impl BlockDownloader { imported.insert(h.clone()); self.block_imported(&h, number, &parent); }, - Err(BlockImportError::Block(BlockError::UnknownParent(_))) if allow_out_of_order => { + Err(BlockImportError(BlockImportErrorKind::Block(BlockError::UnknownParent(_)), _)) if allow_out_of_order => { break; }, - Err(BlockImportError::Block(BlockError::UnknownParent(_))) => { + Err(BlockImportError(BlockImportErrorKind::Block(BlockError::UnknownParent(_)), _)) => { trace!(target: "sync", "Unknown new block parent, restarting sync"); break; }, - Err(BlockImportError::Block(BlockError::TemporarilyInvalid(_))) => { + Err(BlockImportError(BlockImportErrorKind::Block(BlockError::TemporarilyInvalid(_)), _)) => { debug!(target: "sync", "Block temporarily invalid, restarting sync"); break; }, diff --git a/sync/src/blocks.rs b/ethcore/sync/src/blocks.rs similarity index 94% rename from sync/src/blocks.rs rename to ethcore/sync/src/blocks.rs index 65244a4e719076af587183bab750baad650bc5d3..8485b1d75e6626273aa415488cb07d9d298033e3 100644 --- a/sync/src/blocks.rs +++ b/ethcore/sync/src/blocks.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -22,8 +22,10 @@ use heapsize::HeapSizeOf; use ethereum_types::H256; use triehash::ordered_trie_root; use bytes::Bytes; -use rlp::*; +use rlp::{Rlp, RlpStream, DecoderError}; use network; +use ethcore::encoded::Block; +use ethcore::views::{HeaderView, BodyView}; use ethcore::header::Header as BlockHeader; known_heap_size!(0, HeaderId); @@ -192,7 +194,6 @@ impl BlockCollection { needed_bodies } - /// Returns a set of block hashes that require a receipt download. The returned set is marked as being downloaded. pub fn needed_receipts(&mut self, count: usize, _ignore_downloading: bool) -> Vec { if self.head.is_none() || !self.need_receipts { @@ -264,7 +265,7 @@ impl BlockCollection { } } - /// Get a valid chain of blocks ordered in descending order and ready for importing into blockchain. + /// Get a valid chain of blocks ordered in ascending order and ready for importing into blockchain. pub fn drain(&mut self) -> Vec { if self.blocks.is_empty() || self.head.is_none() { return Vec::new(); @@ -290,15 +291,11 @@ impl BlockCollection { } for block in blocks { - let mut block_rlp = RlpStream::new_list(3); - block_rlp.append_raw(&block.header, 1); - { - let body = Rlp::new(block.body.as_ref().expect("blocks contains only full blocks; qed")); - block_rlp.append_raw(body.at(0).as_raw(), 1); - block_rlp.append_raw(body.at(1).as_raw(), 1); - } + let body = view!(BodyView, block.body.as_ref().expect("blocks contains only full blocks; qed")); + let header = view!(HeaderView, &block.header); + let block_view = Block::new_from_header_and_body(&header, &body); drained.push(BlockAndReceipts { - block: block_rlp.out(), + block: block_view.rlp().as_raw().to_vec(), receipts: block.receipts.clone(), }); } @@ -343,7 +340,7 @@ impl BlockCollection { fn insert_body(&mut self, b: Bytes) -> Result<(), network::Error> { let header_id = { - let body = UntrustedRlp::new(&b); + let body = Rlp::new(&b); let tx = body.at(0)?; let tx_root = ordered_trie_root(tx.iter().map(|r| r.as_raw())); let uncles = keccak(body.at(1)?.as_raw()); @@ -378,7 +375,7 @@ impl BlockCollection { fn insert_receipt(&mut self, r: Bytes) -> Result<(), network::Error> { let receipt_root = { - let receipts = UntrustedRlp::new(&r); + let receipts = Rlp::new(&r); ordered_trie_root(receipts.iter().map(|r| r.as_raw())) }; self.downloading_receipts.remove(&receipt_root); @@ -406,7 +403,7 @@ impl BlockCollection { } fn insert_header(&mut self, header: Bytes) -> Result { - let info: BlockHeader = UntrustedRlp::new(&header).as_val()?; + let info: BlockHeader = Rlp::new(&header).as_val()?; let hash = info.hash(); if self.blocks.contains_key(&hash) { return Ok(hash); @@ -528,8 +525,8 @@ mod test { let blocks: Vec<_> = (0..nblocks) .map(|i| (&client as &BlockChainClient).block(BlockId::Number(i as BlockNumber)).unwrap().into_inner()) .collect(); - let headers: Vec<_> = blocks.iter().map(|b| Rlp::new(b).at(0).as_raw().to_vec()).collect(); - let hashes: Vec<_> = headers.iter().map(|h| HeaderView::new(h).hash()).collect(); + let headers: Vec<_> = blocks.iter().map(|b| Rlp::new(b).at(0).unwrap().as_raw().to_vec()).collect(); + let hashes: Vec<_> = headers.iter().map(|h| view!(HeaderView, h).hash()).collect(); let heads: Vec<_> = hashes.iter().enumerate().filter_map(|(i, h)| if i % 20 == 0 { Some(h.clone()) } else { None }).collect(); bc.reset_to(heads); assert!(!bc.is_empty()); @@ -583,8 +580,8 @@ mod test { let blocks: Vec<_> = (0..nblocks) .map(|i| (&client as &BlockChainClient).block(BlockId::Number(i as BlockNumber)).unwrap().into_inner()) .collect(); - let headers: Vec<_> = blocks.iter().map(|b| Rlp::new(b).at(0).as_raw().to_vec()).collect(); - let hashes: Vec<_> = headers.iter().map(|h| HeaderView::new(h).hash()).collect(); + let headers: Vec<_> = blocks.iter().map(|b| Rlp::new(b).at(0).unwrap().as_raw().to_vec()).collect(); + let hashes: Vec<_> = headers.iter().map(|h| view!(HeaderView, h).hash()).collect(); let heads: Vec<_> = hashes.iter().enumerate().filter_map(|(i, h)| if i % 20 == 0 { Some(h.clone()) } else { None }).collect(); bc.reset_to(heads); @@ -607,8 +604,8 @@ mod test { let blocks: Vec<_> = (0..nblocks) .map(|i| (&client as &BlockChainClient).block(BlockId::Number(i as BlockNumber)).unwrap().into_inner()) .collect(); - let headers: Vec<_> = blocks.iter().map(|b| Rlp::new(b).at(0).as_raw().to_vec()).collect(); - let hashes: Vec<_> = headers.iter().map(|h| HeaderView::new(h).hash()).collect(); + let headers: Vec<_> = blocks.iter().map(|b| Rlp::new(b).at(0).unwrap().as_raw().to_vec()).collect(); + let hashes: Vec<_> = headers.iter().map(|h| view!(HeaderView, h).hash()).collect(); let heads: Vec<_> = hashes.iter().enumerate().filter_map(|(i, h)| if i % 20 == 0 { Some(h.clone()) } else { None }).collect(); bc.reset_to(heads); @@ -618,4 +615,3 @@ mod test { assert_eq!(bc.drain().len(), 2); } } - diff --git a/ethcore/sync/src/chain/handler.rs b/ethcore/sync/src/chain/handler.rs new file mode 100644 index 0000000000000000000000000000000000000000..75111a4d4a6a6d0690ce609749ef3cbb7306a193 --- /dev/null +++ b/ethcore/sync/src/chain/handler.rs @@ -0,0 +1,853 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use api::WARP_SYNC_PROTOCOL_ID; +use block_sync::{BlockDownloaderImportError as DownloaderImportError, DownloadAction}; +use bytes::Bytes; +use ethcore::client::{BlockStatus, BlockId, BlockImportError, BlockImportErrorKind}; +use ethcore::error::*; +use ethcore::header::{BlockNumber, Header as BlockHeader}; +use ethcore::snapshot::{ManifestData, RestorationStatus}; +use ethereum_types::{H256, U256}; +use hash::keccak; +use network::PeerId; +use rlp::Rlp; +use snapshot::ChunkType; +use std::cmp; +use std::collections::HashSet; +use std::time::Instant; +use sync_io::SyncIo; + +use super::{ + BlockSet, + ChainSync, + ForkConfirmation, + PacketDecodeError, + PeerAsking, + PeerInfo, + SyncRequester, + SyncState, + ETH_PROTOCOL_VERSION_62, + ETH_PROTOCOL_VERSION_63, + MAX_NEW_BLOCK_AGE, + MAX_NEW_HASHES, + PAR_PROTOCOL_VERSION_1, + PAR_PROTOCOL_VERSION_3, + BLOCK_BODIES_PACKET, + BLOCK_HEADERS_PACKET, + NEW_BLOCK_HASHES_PACKET, + NEW_BLOCK_PACKET, + PRIVATE_TRANSACTION_PACKET, + RECEIPTS_PACKET, + SIGNED_PRIVATE_TRANSACTION_PACKET, + SNAPSHOT_DATA_PACKET, + SNAPSHOT_MANIFEST_PACKET, + STATUS_PACKET, + TRANSACTIONS_PACKET, +}; + +/// The Chain Sync Handler: handles responses from peers +pub struct SyncHandler; + +impl SyncHandler { + /// Handle incoming packet from peer + pub fn on_packet(sync: &mut ChainSync, io: &mut SyncIo, peer: PeerId, packet_id: u8, data: &[u8]) { + if packet_id != STATUS_PACKET && !sync.peers.contains_key(&peer) { + debug!(target:"sync", "Unexpected packet {} from unregistered peer: {}:{}", packet_id, peer, io.peer_info(peer)); + return; + } + let rlp = Rlp::new(data); + let result = match packet_id { + STATUS_PACKET => SyncHandler::on_peer_status(sync, io, peer, &rlp), + TRANSACTIONS_PACKET => SyncHandler::on_peer_transactions(sync, io, peer, &rlp), + BLOCK_HEADERS_PACKET => SyncHandler::on_peer_block_headers(sync, io, peer, &rlp), + BLOCK_BODIES_PACKET => SyncHandler::on_peer_block_bodies(sync, io, peer, &rlp), + RECEIPTS_PACKET => SyncHandler::on_peer_block_receipts(sync, io, peer, &rlp), + NEW_BLOCK_PACKET => SyncHandler::on_peer_new_block(sync, io, peer, &rlp), + NEW_BLOCK_HASHES_PACKET => SyncHandler::on_peer_new_hashes(sync, io, peer, &rlp), + SNAPSHOT_MANIFEST_PACKET => SyncHandler::on_snapshot_manifest(sync, io, peer, &rlp), + SNAPSHOT_DATA_PACKET => SyncHandler::on_snapshot_data(sync, io, peer, &rlp), + PRIVATE_TRANSACTION_PACKET => SyncHandler::on_private_transaction(sync, io, peer, &rlp), + SIGNED_PRIVATE_TRANSACTION_PACKET => SyncHandler::on_signed_private_transaction(sync, io, peer, &rlp), + _ => { + debug!(target: "sync", "{}: Unknown packet {}", peer, packet_id); + Ok(()) + } + }; + result.unwrap_or_else(|e| { + debug!(target:"sync", "{} -> Malformed packet {} : {}", peer, packet_id, e); + }) + } + + /// Called when peer sends us new consensus packet + pub fn on_consensus_packet(io: &mut SyncIo, peer_id: PeerId, r: &Rlp) -> Result<(), PacketDecodeError> { + trace!(target: "sync", "Received consensus packet from {:?}", peer_id); + io.chain().queue_consensus_message(r.as_raw().to_vec()); + Ok(()) + } + + /// Called by peer when it is disconnecting + pub fn on_peer_aborting(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId) { + trace!(target: "sync", "== Disconnecting {}: {}", peer_id, io.peer_info(peer_id)); + sync.handshaking_peers.remove(&peer_id); + if sync.peers.contains_key(&peer_id) { + debug!(target: "sync", "Disconnected {}", peer_id); + sync.clear_peer_download(peer_id); + sync.peers.remove(&peer_id); + sync.active_peers.remove(&peer_id); + + if sync.state == SyncState::SnapshotManifest { + // Check if we are asking other peers for + // the snapshot manifest as well. + // If not, return to initial state + let still_asking_manifest = sync.peers.iter() + .filter(|&(id, p)| sync.active_peers.contains(id) && p.asking == PeerAsking::SnapshotManifest) + .next().is_none(); + + if still_asking_manifest { + sync.state = ChainSync::get_init_state(sync.warp_sync, io.chain()); + } + } + sync.continue_sync(io); + } + } + + /// Called when a new peer is connected + pub fn on_peer_connected(sync: &mut ChainSync, io: &mut SyncIo, peer: PeerId) { + trace!(target: "sync", "== Connected {}: {}", peer, io.peer_info(peer)); + if let Err(e) = sync.send_status(io, peer) { + debug!(target:"sync", "Error sending status request: {:?}", e); + io.disconnect_peer(peer); + } else { + sync.handshaking_peers.insert(peer, Instant::now()); + } + } + + /// Called by peer once it has new block bodies + pub fn on_peer_new_block(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, r: &Rlp) -> Result<(), PacketDecodeError> { + if !sync.peers.get(&peer_id).map_or(false, |p| p.can_sync()) { + trace!(target: "sync", "Ignoring new block from unconfirmed peer {}", peer_id); + return Ok(()); + } + let difficulty: U256 = r.val_at(1)?; + if let Some(ref mut peer) = sync.peers.get_mut(&peer_id) { + if peer.difficulty.map_or(true, |pd| difficulty > pd) { + peer.difficulty = Some(difficulty); + } + } + let block_rlp = r.at(0)?; + let header_rlp = block_rlp.at(0)?; + let h = keccak(&header_rlp.as_raw()); + trace!(target: "sync", "{} -> NewBlock ({})", peer_id, h); + let header: BlockHeader = header_rlp.as_val()?; + if header.number() > sync.highest_block.unwrap_or(0) { + sync.highest_block = Some(header.number()); + } + let mut unknown = false; + { + if let Some(ref mut peer) = sync.peers.get_mut(&peer_id) { + peer.latest_hash = header.hash(); + } + } + let last_imported_number = sync.new_blocks.last_imported_block_number(); + if last_imported_number > header.number() && last_imported_number - header.number() > MAX_NEW_BLOCK_AGE { + trace!(target: "sync", "Ignored ancient new block {:?}", h); + io.disable_peer(peer_id); + return Ok(()); + } + match io.chain().import_block(block_rlp.as_raw().to_vec()) { + Err(BlockImportError(BlockImportErrorKind::Import(ImportErrorKind::AlreadyInChain), _)) => { + trace!(target: "sync", "New block already in chain {:?}", h); + }, + Err(BlockImportError(BlockImportErrorKind::Import(ImportErrorKind::AlreadyQueued), _)) => { + trace!(target: "sync", "New block already queued {:?}", h); + }, + Ok(_) => { + // abort current download of the same block + sync.complete_sync(io); + sync.new_blocks.mark_as_known(&header.hash(), header.number()); + trace!(target: "sync", "New block queued {:?} ({})", h, header.number()); + }, + Err(BlockImportError(BlockImportErrorKind::Block(BlockError::UnknownParent(p)), _)) => { + unknown = true; + trace!(target: "sync", "New block with unknown parent ({:?}) {:?}", p, h); + }, + Err(e) => { + debug!(target: "sync", "Bad new block {:?} : {:?}", h, e); + io.disable_peer(peer_id); + } + }; + if unknown { + if sync.state != SyncState::Idle { + trace!(target: "sync", "NewBlock ignored while seeking"); + } else { + trace!(target: "sync", "New unknown block {:?}", h); + //TODO: handle too many unknown blocks + sync.sync_peer(io, peer_id, true); + } + } + sync.continue_sync(io); + Ok(()) + } + + /// Handles `NewHashes` packet. Initiates headers download for any unknown hashes. + pub fn on_peer_new_hashes(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, r: &Rlp) -> Result<(), PacketDecodeError> { + if !sync.peers.get(&peer_id).map_or(false, |p| p.can_sync()) { + trace!(target: "sync", "Ignoring new hashes from unconfirmed peer {}", peer_id); + return Ok(()); + } + let hashes: Vec<_> = r.iter().take(MAX_NEW_HASHES).map(|item| (item.val_at::(0), item.val_at::(1))).collect(); + if let Some(ref mut peer) = sync.peers.get_mut(&peer_id) { + // Peer has new blocks with unknown difficulty + peer.difficulty = None; + if let Some(&(Ok(ref h), _)) = hashes.last() { + peer.latest_hash = h.clone(); + } + } + if sync.state != SyncState::Idle { + trace!(target: "sync", "Ignoring new hashes since we're already downloading."); + let max = r.iter().take(MAX_NEW_HASHES).map(|item| item.val_at::(1).unwrap_or(0)).fold(0u64, cmp::max); + if max > sync.highest_block.unwrap_or(0) { + sync.highest_block = Some(max); + } + sync.continue_sync(io); + return Ok(()); + } + trace!(target: "sync", "{} -> NewHashes ({} entries)", peer_id, r.item_count()?); + let mut max_height: BlockNumber = 0; + let mut new_hashes = Vec::new(); + let last_imported_number = sync.new_blocks.last_imported_block_number(); + for (rh, rn) in hashes { + let hash = rh?; + let number = rn?; + if number > sync.highest_block.unwrap_or(0) { + sync.highest_block = Some(number); + } + if sync.new_blocks.is_downloading(&hash) { + continue; + } + if last_imported_number > number && last_imported_number - number > MAX_NEW_BLOCK_AGE { + trace!(target: "sync", "Ignored ancient new block hash {:?}", hash); + io.disable_peer(peer_id); + continue; + } + match io.chain().block_status(BlockId::Hash(hash.clone())) { + BlockStatus::InChain => { + trace!(target: "sync", "New block hash already in chain {:?}", hash); + }, + BlockStatus::Queued => { + trace!(target: "sync", "New hash block already queued {:?}", hash); + }, + BlockStatus::Unknown | BlockStatus::Pending => { + new_hashes.push(hash.clone()); + if number > max_height { + trace!(target: "sync", "New unknown block hash {:?}", hash); + if let Some(ref mut peer) = sync.peers.get_mut(&peer_id) { + peer.latest_hash = hash.clone(); + } + max_height = number; + } + }, + BlockStatus::Bad => { + debug!(target: "sync", "Bad new block hash {:?}", hash); + io.disable_peer(peer_id); + return Ok(()); + } + } + }; + if max_height != 0 { + trace!(target: "sync", "Downloading blocks for new hashes"); + sync.new_blocks.reset_to(new_hashes); + sync.state = SyncState::NewBlocks; + sync.sync_peer(io, peer_id, true); + } + sync.continue_sync(io); + Ok(()) + } + + /// Called by peer once it has new block bodies + fn on_peer_block_bodies(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, r: &Rlp) -> Result<(), PacketDecodeError> { + sync.clear_peer_download(peer_id); + let block_set = sync.peers.get(&peer_id).and_then(|p| p.block_set).unwrap_or(BlockSet::NewBlocks); + if !sync.reset_peer_asking(peer_id, PeerAsking::BlockBodies) { + trace!(target: "sync", "{}: Ignored unexpected bodies", peer_id); + sync.continue_sync(io); + return Ok(()); + } + let item_count = r.item_count()?; + trace!(target: "sync", "{} -> BlockBodies ({} entries), set = {:?}", peer_id, item_count, block_set); + if item_count == 0 { + sync.deactivate_peer(io, peer_id); + } + else if sync.state == SyncState::Waiting { + trace!(target: "sync", "Ignored block bodies while waiting"); + } + else + { + let result = { + let downloader = match block_set { + BlockSet::NewBlocks => &mut sync.new_blocks, + BlockSet::OldBlocks => match sync.old_blocks { + None => { + trace!(target: "sync", "Ignored block headers while block download is inactive"); + sync.continue_sync(io); + return Ok(()); + }, + Some(ref mut blocks) => blocks, + } + }; + downloader.import_bodies(io, r) + }; + + match result { + Err(DownloaderImportError::Invalid) => { + io.disable_peer(peer_id); + sync.deactivate_peer(io, peer_id); + sync.continue_sync(io); + return Ok(()); + }, + Err(DownloaderImportError::Useless) => { + sync.deactivate_peer(io, peer_id); + }, + Ok(()) => (), + } + + sync.collect_blocks(io, block_set); + sync.sync_peer(io, peer_id, false); + } + sync.continue_sync(io); + Ok(()) + } + + fn on_peer_fork_header(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, r: &Rlp) -> Result<(), PacketDecodeError> { + { + let peer = sync.peers.get_mut(&peer_id).expect("Is only called when peer is present in peers"); + peer.asking = PeerAsking::Nothing; + let item_count = r.item_count()?; + let (fork_number, fork_hash) = sync.fork_block.expect("ForkHeader request is sent only fork block is Some; qed").clone(); + + if item_count == 0 || item_count != 1 { + trace!(target: "sync", "{}: Chain is too short to confirm the block", peer_id); + peer.confirmation = ForkConfirmation::TooShort; + + } else { + let header = r.at(0)?.as_raw(); + if keccak(&header) != fork_hash { + trace!(target: "sync", "{}: Fork mismatch", peer_id); + io.disable_peer(peer_id); + return Ok(()); + } + + trace!(target: "sync", "{}: Confirmed peer", peer_id); + peer.confirmation = ForkConfirmation::Confirmed; + + if !io.chain_overlay().read().contains_key(&fork_number) { + trace!(target: "sync", "Inserting (fork) block {} header", fork_number); + io.chain_overlay().write().insert(fork_number, header.to_vec()); + } + } + } + + sync.sync_peer(io, peer_id, false); + return Ok(()); + } + + /// Called by peer once it has new block headers during sync + fn on_peer_block_headers(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, r: &Rlp) -> Result<(), PacketDecodeError> { + let is_fork_header_request = match sync.peers.get(&peer_id) { + Some(peer) if peer.asking == PeerAsking::ForkHeader => true, + _ => false, + }; + + if is_fork_header_request { + return SyncHandler::on_peer_fork_header(sync, io, peer_id, r); + } + + sync.clear_peer_download(peer_id); + let expected_hash = sync.peers.get(&peer_id).and_then(|p| p.asking_hash); + let allowed = sync.peers.get(&peer_id).map(|p| p.is_allowed()).unwrap_or(false); + let block_set = sync.peers.get(&peer_id).and_then(|p| p.block_set).unwrap_or(BlockSet::NewBlocks); + if !sync.reset_peer_asking(peer_id, PeerAsking::BlockHeaders) || expected_hash.is_none() || !allowed { + trace!(target: "sync", "{}: Ignored unexpected headers, expected_hash = {:?}", peer_id, expected_hash); + sync.continue_sync(io); + return Ok(()); + } + let item_count = r.item_count()?; + trace!(target: "sync", "{} -> BlockHeaders ({} entries), state = {:?}, set = {:?}", peer_id, item_count, sync.state, block_set); + if (sync.state == SyncState::Idle || sync.state == SyncState::WaitingPeers) && sync.old_blocks.is_none() { + trace!(target: "sync", "Ignored unexpected block headers"); + sync.continue_sync(io); + return Ok(()); + } + if sync.state == SyncState::Waiting { + trace!(target: "sync", "Ignored block headers while waiting"); + sync.continue_sync(io); + return Ok(()); + } + + let result = { + let downloader = match block_set { + BlockSet::NewBlocks => &mut sync.new_blocks, + BlockSet::OldBlocks => { + match sync.old_blocks { + None => { + trace!(target: "sync", "Ignored block headers while block download is inactive"); + sync.continue_sync(io); + return Ok(()); + }, + Some(ref mut blocks) => blocks, + } + } + }; + downloader.import_headers(io, r, expected_hash) + }; + + match result { + Err(DownloaderImportError::Useless) => { + sync.deactivate_peer(io, peer_id); + }, + Err(DownloaderImportError::Invalid) => { + io.disable_peer(peer_id); + sync.deactivate_peer(io, peer_id); + sync.continue_sync(io); + return Ok(()); + }, + Ok(DownloadAction::Reset) => { + // mark all outstanding requests as expired + trace!("Resetting downloads for {:?}", block_set); + for (_, ref mut p) in sync.peers.iter_mut().filter(|&(_, ref p)| p.block_set == Some(block_set)) { + p.reset_asking(); + } + + } + Ok(DownloadAction::None) => {}, + } + + sync.collect_blocks(io, block_set); + // give a task to the same peer first if received valuable headers. + sync.sync_peer(io, peer_id, false); + // give tasks to other peers + sync.continue_sync(io); + Ok(()) + } + + /// Called by peer once it has new block receipts + fn on_peer_block_receipts(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, r: &Rlp) -> Result<(), PacketDecodeError> { + sync.clear_peer_download(peer_id); + let block_set = sync.peers.get(&peer_id).and_then(|p| p.block_set).unwrap_or(BlockSet::NewBlocks); + if !sync.reset_peer_asking(peer_id, PeerAsking::BlockReceipts) { + trace!(target: "sync", "{}: Ignored unexpected receipts", peer_id); + sync.continue_sync(io); + return Ok(()); + } + let item_count = r.item_count()?; + trace!(target: "sync", "{} -> BlockReceipts ({} entries)", peer_id, item_count); + if item_count == 0 { + sync.deactivate_peer(io, peer_id); + } + else if sync.state == SyncState::Waiting { + trace!(target: "sync", "Ignored block receipts while waiting"); + } + else + { + let result = { + let downloader = match block_set { + BlockSet::NewBlocks => &mut sync.new_blocks, + BlockSet::OldBlocks => match sync.old_blocks { + None => { + trace!(target: "sync", "Ignored block headers while block download is inactive"); + sync.continue_sync(io); + return Ok(()); + }, + Some(ref mut blocks) => blocks, + } + }; + downloader.import_receipts(io, r) + }; + + match result { + Err(DownloaderImportError::Invalid) => { + io.disable_peer(peer_id); + sync.deactivate_peer(io, peer_id); + sync.continue_sync(io); + return Ok(()); + }, + Err(DownloaderImportError::Useless) => { + sync.deactivate_peer(io, peer_id); + }, + Ok(()) => (), + } + + sync.collect_blocks(io, block_set); + sync.sync_peer(io, peer_id, false); + } + sync.continue_sync(io); + Ok(()) + } + + /// Called when snapshot manifest is downloaded from a peer. + fn on_snapshot_manifest(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, r: &Rlp) -> Result<(), PacketDecodeError> { + if !sync.peers.get(&peer_id).map_or(false, |p| p.can_sync()) { + trace!(target: "sync", "Ignoring snapshot manifest from unconfirmed peer {}", peer_id); + return Ok(()); + } + sync.clear_peer_download(peer_id); + if !sync.reset_peer_asking(peer_id, PeerAsking::SnapshotManifest) || sync.state != SyncState::SnapshotManifest { + trace!(target: "sync", "{}: Ignored unexpected/expired manifest", peer_id); + sync.continue_sync(io); + return Ok(()); + } + + let manifest_rlp = r.at(0)?; + let manifest = match ManifestData::from_rlp(manifest_rlp.as_raw()) { + Err(e) => { + trace!(target: "sync", "{}: Ignored bad manifest: {:?}", peer_id, e); + io.disable_peer(peer_id); + sync.continue_sync(io); + return Ok(()); + } + Ok(manifest) => manifest, + }; + + let is_supported_version = io.snapshot_service().supported_versions() + .map_or(false, |(l, h)| manifest.version >= l && manifest.version <= h); + + if !is_supported_version { + trace!(target: "sync", "{}: Snapshot manifest version not supported: {}", peer_id, manifest.version); + io.disable_peer(peer_id); + sync.continue_sync(io); + return Ok(()); + } + sync.snapshot.reset_to(&manifest, &keccak(manifest_rlp.as_raw())); + io.snapshot_service().begin_restore(manifest); + sync.state = SyncState::SnapshotData; + + // give a task to the same peer first. + sync.sync_peer(io, peer_id, false); + // give tasks to other peers + sync.continue_sync(io); + Ok(()) + } + + /// Called when snapshot data is downloaded from a peer. + fn on_snapshot_data(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, r: &Rlp) -> Result<(), PacketDecodeError> { + if !sync.peers.get(&peer_id).map_or(false, |p| p.can_sync()) { + trace!(target: "sync", "Ignoring snapshot data from unconfirmed peer {}", peer_id); + return Ok(()); + } + sync.clear_peer_download(peer_id); + if !sync.reset_peer_asking(peer_id, PeerAsking::SnapshotData) || (sync.state != SyncState::SnapshotData && sync.state != SyncState::SnapshotWaiting) { + trace!(target: "sync", "{}: Ignored unexpected snapshot data", peer_id); + sync.continue_sync(io); + return Ok(()); + } + + // check service status + let status = io.snapshot_service().status(); + match status { + RestorationStatus::Inactive | RestorationStatus::Failed => { + trace!(target: "sync", "{}: Snapshot restoration aborted", peer_id); + sync.state = SyncState::WaitingPeers; + + // only note bad if restoration failed. + if let (Some(hash), RestorationStatus::Failed) = (sync.snapshot.snapshot_hash(), status) { + trace!(target: "sync", "Noting snapshot hash {} as bad", hash); + sync.snapshot.note_bad(hash); + } + + sync.snapshot.clear(); + sync.continue_sync(io); + return Ok(()); + }, + RestorationStatus::Initializing { .. } => { + trace!(target: "warp", "{}: Snapshot restoration is initializing", peer_id); + return Ok(()); + } + RestorationStatus::Ongoing { .. } => { + trace!(target: "sync", "{}: Snapshot restoration is ongoing", peer_id); + }, + } + + let snapshot_data: Bytes = r.val_at(0)?; + match sync.snapshot.validate_chunk(&snapshot_data) { + Ok(ChunkType::Block(hash)) => { + trace!(target: "sync", "{}: Processing block chunk", peer_id); + io.snapshot_service().restore_block_chunk(hash, snapshot_data); + } + Ok(ChunkType::State(hash)) => { + trace!(target: "sync", "{}: Processing state chunk", peer_id); + io.snapshot_service().restore_state_chunk(hash, snapshot_data); + } + Err(()) => { + trace!(target: "sync", "{}: Got bad snapshot chunk", peer_id); + io.disconnect_peer(peer_id); + sync.continue_sync(io); + return Ok(()); + } + } + + if sync.snapshot.is_complete() { + // wait for snapshot restoration process to complete + sync.state = SyncState::SnapshotWaiting; + } + // give a task to the same peer first. + sync.sync_peer(io, peer_id, false); + // give tasks to other peers + sync.continue_sync(io); + Ok(()) + } + + /// Called by peer to report status + fn on_peer_status(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, r: &Rlp) -> Result<(), PacketDecodeError> { + sync.handshaking_peers.remove(&peer_id); + let protocol_version: u8 = r.val_at(0)?; + let warp_protocol = io.protocol_version(&WARP_SYNC_PROTOCOL_ID, peer_id) != 0; + let peer = PeerInfo { + protocol_version: protocol_version, + network_id: r.val_at(1)?, + difficulty: Some(r.val_at(2)?), + latest_hash: r.val_at(3)?, + genesis: r.val_at(4)?, + asking: PeerAsking::Nothing, + asking_blocks: Vec::new(), + asking_hash: None, + ask_time: Instant::now(), + last_sent_transactions: HashSet::new(), + expired: false, + confirmation: if sync.fork_block.is_none() { ForkConfirmation::Confirmed } else { ForkConfirmation::Unconfirmed }, + asking_snapshot_data: None, + snapshot_hash: if warp_protocol { Some(r.val_at(5)?) } else { None }, + snapshot_number: if warp_protocol { Some(r.val_at(6)?) } else { None }, + block_set: None, + }; + + trace!(target: "sync", "New peer {} (protocol: {}, network: {:?}, difficulty: {:?}, latest:{}, genesis:{}, snapshot:{:?})", + peer_id, peer.protocol_version, peer.network_id, peer.difficulty, peer.latest_hash, peer.genesis, peer.snapshot_number); + if io.is_expired() { + trace!(target: "sync", "Status packet from expired session {}:{}", peer_id, io.peer_info(peer_id)); + return Ok(()); + } + + if sync.peers.contains_key(&peer_id) { + debug!(target: "sync", "Unexpected status packet from {}:{}", peer_id, io.peer_info(peer_id)); + return Ok(()); + } + let chain_info = io.chain().chain_info(); + if peer.genesis != chain_info.genesis_hash { + io.disable_peer(peer_id); + trace!(target: "sync", "Peer {} genesis hash mismatch (ours: {}, theirs: {})", peer_id, chain_info.genesis_hash, peer.genesis); + return Ok(()); + } + if peer.network_id != sync.network_id { + io.disable_peer(peer_id); + trace!(target: "sync", "Peer {} network id mismatch (ours: {}, theirs: {})", peer_id, sync.network_id, peer.network_id); + return Ok(()); + } + + if false + || (warp_protocol && (peer.protocol_version < PAR_PROTOCOL_VERSION_1.0 || peer.protocol_version > PAR_PROTOCOL_VERSION_3.0)) + || (!warp_protocol && (peer.protocol_version < ETH_PROTOCOL_VERSION_62.0 || peer.protocol_version > ETH_PROTOCOL_VERSION_63.0)) + { + io.disable_peer(peer_id); + trace!(target: "sync", "Peer {} unsupported eth protocol ({})", peer_id, peer.protocol_version); + return Ok(()); + } + + if sync.sync_start_time.is_none() { + sync.sync_start_time = Some(Instant::now()); + } + + sync.peers.insert(peer_id.clone(), peer); + // Don't activate peer immediatelly when searching for common block. + // Let the current sync round complete first. + sync.active_peers.insert(peer_id.clone()); + debug!(target: "sync", "Connected {}:{}", peer_id, io.peer_info(peer_id)); + + match sync.fork_block { + Some((fork_block, _)) => { + SyncRequester::request_fork_header(sync, io, peer_id, fork_block); + }, + _ => { + // when there's no `fork_block` defined we initialize the peer with + // `confirmation: ForkConfirmation::Confirmed`. + sync.sync_peer(io, peer_id, false); + } + } + + Ok(()) + } + + /// Called when peer sends us new transactions + fn on_peer_transactions(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, r: &Rlp) -> Result<(), PacketDecodeError> { + // Accept transactions only when fully synced + if !io.is_chain_queue_empty() || (sync.state != SyncState::Idle && sync.state != SyncState::NewBlocks) { + trace!(target: "sync", "{} Ignoring transactions while syncing", peer_id); + return Ok(()); + } + if !sync.peers.get(&peer_id).map_or(false, |p| p.can_sync()) { + trace!(target: "sync", "{} Ignoring transactions from unconfirmed/unknown peer", peer_id); + return Ok(()); + } + + let item_count = r.item_count()?; + trace!(target: "sync", "{:02} -> Transactions ({} entries)", peer_id, item_count); + let mut transactions = Vec::with_capacity(item_count); + for i in 0 .. item_count { + let rlp = r.at(i)?; + let tx = rlp.as_raw().to_vec(); + transactions.push(tx); + } + io.chain().queue_transactions(transactions, peer_id); + Ok(()) + } + + /// Called when peer sends us signed private transaction packet + fn on_signed_private_transaction(sync: &ChainSync, _io: &mut SyncIo, peer_id: PeerId, r: &Rlp) -> Result<(), PacketDecodeError> { + if !sync.peers.get(&peer_id).map_or(false, |p| p.can_sync()) { + trace!(target: "sync", "{} Ignoring packet from unconfirmed/unknown peer", peer_id); + return Ok(()); + } + + trace!(target: "sync", "Received signed private transaction packet from {:?}", peer_id); + if let Err(e) = sync.private_tx_handler.import_signed_private_transaction(r.as_raw()) { + trace!(target: "sync", "Ignoring the message, error queueing: {}", e); + } + Ok(()) + } + + /// Called when peer sends us new private transaction packet + fn on_private_transaction(sync: &ChainSync, _io: &mut SyncIo, peer_id: PeerId, r: &Rlp) -> Result<(), PacketDecodeError> { + if !sync.peers.get(&peer_id).map_or(false, |p| p.can_sync()) { + trace!(target: "sync", "{} Ignoring packet from unconfirmed/unknown peer", peer_id); + return Ok(()); + } + + trace!(target: "sync", "Received private transaction packet from {:?}", peer_id); + + if let Err(e) = sync.private_tx_handler.import_private_transaction(r.as_raw()) { + trace!(target: "sync", "Ignoring the message, error queueing: {}", e); + } + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use ethcore::client::{ChainInfo, EachBlockWith, TestBlockChainClient}; + use parking_lot::RwLock; + use rlp::{Rlp}; + use std::collections::{VecDeque}; + use tests::helpers::{TestIo}; + use tests::snapshot::TestSnapshotService; + + use super::*; + use super::super::tests::{ + dummy_sync_with_peer, + get_dummy_block, + get_dummy_blocks, + get_dummy_hashes, + }; + + #[test] + fn handles_peer_new_hashes() { + let mut client = TestBlockChainClient::new(); + client.add_blocks(10, EachBlockWith::Uncle); + let queue = RwLock::new(VecDeque::new()); + let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); + let ss = TestSnapshotService::new(); + let mut io = TestIo::new(&mut client, &ss, &queue, None); + + let hashes_data = get_dummy_hashes(); + let hashes_rlp = Rlp::new(&hashes_data); + + let result = SyncHandler::on_peer_new_hashes(&mut sync, &mut io, 0, &hashes_rlp); + + assert!(result.is_ok()); + } + + #[test] + fn handles_peer_new_block_malformed() { + let mut client = TestBlockChainClient::new(); + client.add_blocks(10, EachBlockWith::Uncle); + + let block_data = get_dummy_block(11, client.chain_info().best_block_hash); + + let queue = RwLock::new(VecDeque::new()); + let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); + //sync.have_common_block = true; + let ss = TestSnapshotService::new(); + let mut io = TestIo::new(&mut client, &ss, &queue, None); + + let block = Rlp::new(&block_data); + + let result = SyncHandler::on_peer_new_block(&mut sync, &mut io, 0, &block); + + assert!(result.is_err()); + } + + #[test] + fn handles_peer_new_block() { + let mut client = TestBlockChainClient::new(); + client.add_blocks(10, EachBlockWith::Uncle); + + let block_data = get_dummy_blocks(11, client.chain_info().best_block_hash); + + let queue = RwLock::new(VecDeque::new()); + let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); + let ss = TestSnapshotService::new(); + let mut io = TestIo::new(&mut client, &ss, &queue, None); + + let block = Rlp::new(&block_data); + + let result = SyncHandler::on_peer_new_block(&mut sync, &mut io, 0, &block); + + assert!(result.is_ok()); + } + + #[test] + fn handles_peer_new_block_empty() { + let mut client = TestBlockChainClient::new(); + client.add_blocks(10, EachBlockWith::Uncle); + let queue = RwLock::new(VecDeque::new()); + let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); + let ss = TestSnapshotService::new(); + let mut io = TestIo::new(&mut client, &ss, &queue, None); + + let empty_data = vec![]; + let block = Rlp::new(&empty_data); + + let result = SyncHandler::on_peer_new_block(&mut sync, &mut io, 0, &block); + + assert!(result.is_err()); + } + + #[test] + fn handles_peer_new_hashes_empty() { + let mut client = TestBlockChainClient::new(); + client.add_blocks(10, EachBlockWith::Uncle); + let queue = RwLock::new(VecDeque::new()); + let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); + let ss = TestSnapshotService::new(); + let mut io = TestIo::new(&mut client, &ss, &queue, None); + + let empty_hashes_data = vec![]; + let hashes_rlp = Rlp::new(&empty_hashes_data); + + let result = SyncHandler::on_peer_new_hashes(&mut sync, &mut io, 0, &hashes_rlp); + + assert!(result.is_ok()); + } +} diff --git a/ethcore/sync/src/chain/mod.rs b/ethcore/sync/src/chain/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..84e6344e68847a65bd90ae4b4dc7716eb18dd448 --- /dev/null +++ b/ethcore/sync/src/chain/mod.rs @@ -0,0 +1,1406 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! `BlockChain` synchronization strategy. +//! Syncs to peers and keeps up to date. +//! This implementation uses ethereum protocol v63 +//! +//! Syncing strategy summary. +//! Split the chain into ranges of N blocks each. Download ranges sequentially. Split each range into subchains of M blocks. Download subchains in parallel. +//! State. +//! Sync state consists of the following data: +//! - s: State enum which can be one of the following values: `ChainHead`, `Blocks`, `Idle` +//! - H: A set of downloaded block headers +//! - B: A set of downloaded block bodies +//! - S: Set of block subchain start block hashes to download. +//! - l: Last imported / common block hash +//! - P: A set of connected peers. For each peer we maintain its last known total difficulty and starting block hash being requested if any. +//! General behaviour. +//! We start with all sets empty, l is set to the best block in the block chain, s is set to `ChainHead`. +//! If at any moment a bad block is reported by the block queue, we set s to `ChainHead`, reset l to the best block in the block chain and clear H, B and S. +//! If at any moment P becomes empty, we set s to `ChainHead`, and clear H, B and S. +//! +//! Workflow for `ChainHead` state. +//! In this state we try to get subchain headers with a single `GetBlockHeaders` request. +//! On `NewPeer` / On `Restart`: +//! If peer's total difficulty is higher and there are less than 5 peers downloading, request N/M headers with interval M+1 starting from l +//! On `BlockHeaders(R)`: +//! If R is empty: +//! If l is equal to genesis block hash or l is more than 1000 blocks behind our best hash: +//! Remove current peer from P. set l to the best block in the block chain. Select peer with maximum total difficulty from P and restart. +//! Else +//! Set l to l’s parent and restart. +//! Else if we already have all the headers in the block chain or the block queue: +//! Set s to `Idle`, +//! Else +//! Set S to R, set s to `Blocks`. +//! +//! All other messages are ignored. +//! +//! Workflow for `Blocks` state. +//! In this state we download block headers and bodies from multiple peers. +//! On `NewPeer` / On `Restart`: +//! For all idle peers: +//! Find a set of 256 or less block hashes in H which are not in B and not being downloaded by other peers. If the set is not empty: +//! Request block bodies for the hashes in the set. +//! Else +//! Find an element in S which is not being downloaded by other peers. If found: Request M headers starting from the element. +//! +//! On `BlockHeaders(R)`: +//! If R is empty remove current peer from P and restart. +//! Validate received headers: +//! For each header find a parent in H or R or the blockchain. Restart if there is a block with unknown parent. +//! Find at least one header from the received list in S. Restart if there is none. +//! Go to `CollectBlocks`. +//! +//! On `BlockBodies(R)`: +//! If R is empty remove current peer from P and restart. +//! Add bodies with a matching header in H to B. +//! Go to `CollectBlocks`. +//! +//! `CollectBlocks`: +//! Find a chain of blocks C in H starting from h where h’s parent equals to l. The chain ends with the first block which does not have a body in B. +//! Add all blocks from the chain to the block queue. Remove them from H and B. Set l to the hash of the last block from C. +//! Update and merge subchain heads in S. For each h in S find a chain of blocks in B starting from h. Remove h from S. if the chain does not include an element from S add the end of the chain to S. +//! If H is empty and S contains a single element set s to `ChainHead`. +//! Restart. +//! +//! All other messages are ignored. +//! Workflow for Idle state. +//! On `NewBlock`: +//! Import the block. If the block is unknown set s to `ChainHead` and restart. +//! On `NewHashes`: +//! Set s to `ChainHead` and restart. +//! +//! All other messages are ignored. + +mod handler; +mod propagator; +mod requester; +mod supplier; + +use std::sync::Arc; +use std::collections::{HashSet, HashMap}; +use std::cmp; +use std::time::{Duration, Instant}; +use hash::keccak; +use heapsize::HeapSizeOf; +use ethereum_types::{H256, U256}; +use plain_hasher::H256FastMap; +use parking_lot::RwLock; +use bytes::Bytes; +use rlp::{Rlp, RlpStream, DecoderError}; +use network::{self, PeerId, PacketId}; +use ethcore::header::{BlockNumber}; +use ethcore::client::{BlockChainClient, BlockStatus, BlockId, BlockChainInfo, BlockQueueInfo}; +use ethcore::snapshot::{RestorationStatus}; +use sync_io::SyncIo; +use super::{WarpSync, SyncConfig}; +use block_sync::{BlockDownloader, BlockDownloaderImportError as DownloaderImportError}; +use rand::Rng; +use snapshot::{Snapshot}; +use api::{EthProtocolInfo as PeerInfoDigest, WARP_SYNC_PROTOCOL_ID}; +use private_tx::PrivateTxHandler; +use transactions_stats::{TransactionsStats, Stats as TransactionStats}; +use transaction::UnverifiedTransaction; + +use self::handler::SyncHandler; +use self::propagator::SyncPropagator; +use self::requester::SyncRequester; +use self::supplier::SyncSupplier; + +known_heap_size!(0, PeerInfo); + +pub type PacketDecodeError = DecoderError; + +/// 63 version of Ethereum protocol. +pub const ETH_PROTOCOL_VERSION_63: (u8, u8) = (63, 0x11); +/// 62 version of Ethereum protocol. +pub const ETH_PROTOCOL_VERSION_62: (u8, u8) = (62, 0x11); +/// 1 version of Parity protocol and the packet count. +pub const PAR_PROTOCOL_VERSION_1: (u8, u8) = (1, 0x15); +/// 2 version of Parity protocol (consensus messages added). +pub const PAR_PROTOCOL_VERSION_2: (u8, u8) = (2, 0x16); +/// 3 version of Parity protocol (private transactions messages added). +pub const PAR_PROTOCOL_VERSION_3: (u8, u8) = (3, 0x18); + +pub const MAX_BODIES_TO_SEND: usize = 256; +pub const MAX_HEADERS_TO_SEND: usize = 512; +pub const MAX_NODE_DATA_TO_SEND: usize = 1024; +pub const MAX_RECEIPTS_TO_SEND: usize = 1024; +pub const MAX_RECEIPTS_HEADERS_TO_SEND: usize = 256; +const MIN_PEERS_PROPAGATION: usize = 4; +const MAX_PEERS_PROPAGATION: usize = 128; +const MAX_PEER_LAG_PROPAGATION: BlockNumber = 20; +const MAX_NEW_HASHES: usize = 64; +const MAX_NEW_BLOCK_AGE: BlockNumber = 20; +// maximal packet size with transactions (cannot be greater than 16MB - protocol limitation). +const MAX_TRANSACTION_PACKET_SIZE: usize = 8 * 1024 * 1024; +// Maximal number of transactions queried from miner to propagate. +// This set is used to diff with transactions known by the peer and +// we will send a difference of length up to `MAX_TRANSACTIONS_TO_PROPAGATE`. +const MAX_TRANSACTIONS_TO_QUERY: usize = 4096; +// Maximal number of transactions in sent in single packet. +const MAX_TRANSACTIONS_TO_PROPAGATE: usize = 64; +// Min number of blocks to be behind for a snapshot sync +const SNAPSHOT_RESTORE_THRESHOLD: BlockNumber = 30000; +const SNAPSHOT_MIN_PEERS: usize = 3; + +const STATUS_PACKET: u8 = 0x00; +const NEW_BLOCK_HASHES_PACKET: u8 = 0x01; +const TRANSACTIONS_PACKET: u8 = 0x02; +pub const GET_BLOCK_HEADERS_PACKET: u8 = 0x03; +pub const BLOCK_HEADERS_PACKET: u8 = 0x04; +pub const GET_BLOCK_BODIES_PACKET: u8 = 0x05; +const BLOCK_BODIES_PACKET: u8 = 0x06; +const NEW_BLOCK_PACKET: u8 = 0x07; + +pub const GET_NODE_DATA_PACKET: u8 = 0x0d; +pub const NODE_DATA_PACKET: u8 = 0x0e; +pub const GET_RECEIPTS_PACKET: u8 = 0x0f; +pub const RECEIPTS_PACKET: u8 = 0x10; + +pub const GET_SNAPSHOT_MANIFEST_PACKET: u8 = 0x11; +pub const SNAPSHOT_MANIFEST_PACKET: u8 = 0x12; +pub const GET_SNAPSHOT_DATA_PACKET: u8 = 0x13; +pub const SNAPSHOT_DATA_PACKET: u8 = 0x14; +pub const CONSENSUS_DATA_PACKET: u8 = 0x15; +const PRIVATE_TRANSACTION_PACKET: u8 = 0x16; +const SIGNED_PRIVATE_TRANSACTION_PACKET: u8 = 0x17; + +const MAX_SNAPSHOT_CHUNKS_DOWNLOAD_AHEAD: usize = 3; + +const WAIT_PEERS_TIMEOUT: Duration = Duration::from_secs(5); +const STATUS_TIMEOUT: Duration = Duration::from_secs(5); +const HEADERS_TIMEOUT: Duration = Duration::from_secs(15); +const BODIES_TIMEOUT: Duration = Duration::from_secs(20); +const RECEIPTS_TIMEOUT: Duration = Duration::from_secs(10); +const FORK_HEADER_TIMEOUT: Duration = Duration::from_secs(3); +const SNAPSHOT_MANIFEST_TIMEOUT: Duration = Duration::from_secs(5); +const SNAPSHOT_DATA_TIMEOUT: Duration = Duration::from_secs(120); + +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +/// Sync state +pub enum SyncState { + /// Collecting enough peers to start syncing. + WaitingPeers, + /// Waiting for snapshot manifest download + SnapshotManifest, + /// Downloading snapshot data + SnapshotData, + /// Waiting for snapshot restoration progress. + SnapshotWaiting, + /// Downloading new blocks + Blocks, + /// Initial chain sync complete. Waiting for new packets + Idle, + /// Block downloading paused. Waiting for block queue to process blocks and free some space + Waiting, + /// Downloading blocks learned from `NewHashes` packet + NewBlocks, +} + +/// Syncing status and statistics +#[derive(Clone, Copy)] +pub struct SyncStatus { + /// State + pub state: SyncState, + /// Syncing protocol version. That's the maximum protocol version we connect to. + pub protocol_version: u8, + /// The underlying p2p network version. + pub network_id: u64, + /// `BlockChain` height for the moment the sync started. + pub start_block_number: BlockNumber, + /// Last fully downloaded and imported block number (if any). + pub last_imported_block_number: Option, + /// Highest block number in the download queue (if any). + pub highest_block_number: Option, + /// Total number of blocks for the sync process. + pub blocks_total: BlockNumber, + /// Number of blocks downloaded so far. + pub blocks_received: BlockNumber, + /// Total number of connected peers + pub num_peers: usize, + /// Total number of active peers. + pub num_active_peers: usize, + /// Heap memory used in bytes. + pub mem_used: usize, + /// Snapshot chunks + pub num_snapshot_chunks: usize, + /// Snapshot chunks downloaded + pub snapshot_chunks_done: usize, + /// Last fully downloaded and imported ancient block number (if any). + pub last_imported_old_block_number: Option, +} + +impl SyncStatus { + /// Indicates if snapshot download is in progress + pub fn is_snapshot_syncing(&self) -> bool { + match self.state { + SyncState::SnapshotManifest | + SyncState::SnapshotData | + SyncState::SnapshotWaiting => true, + _ => false, + } + } + + /// Returns max no of peers to display in informants + pub fn current_max_peers(&self, min_peers: u32, max_peers: u32) -> u32 { + if self.num_peers as u32 > min_peers { + max_peers + } else { + min_peers + } + } + + /// Is it doing a major sync? + pub fn is_syncing(&self, queue_info: BlockQueueInfo) -> bool { + let is_syncing_state = match self.state { SyncState::Idle | SyncState::NewBlocks => false, _ => true }; + let is_verifying = queue_info.unverified_queue_size + queue_info.verified_queue_size > 3; + is_verifying || is_syncing_state + } +} + +#[derive(PartialEq, Eq, Debug, Clone)] +/// Peer data type requested +pub enum PeerAsking { + Nothing, + ForkHeader, + BlockHeaders, + BlockBodies, + BlockReceipts, + SnapshotManifest, + SnapshotData, +} + +#[derive(PartialEq, Eq, Debug, Clone, Copy)] +/// Block downloader channel. +pub enum BlockSet { + /// New blocks better than out best blocks + NewBlocks, + /// Missing old blocks + OldBlocks, +} +#[derive(Clone, Eq, PartialEq)] +pub enum ForkConfirmation { + /// Fork block confirmation pending. + Unconfirmed, + /// Peer's chain is too short to confirm the fork. + TooShort, + /// Fork is confirmed. + Confirmed, +} + +#[derive(Clone)] +/// Syncing peer information +pub struct PeerInfo { + /// eth protocol version + protocol_version: u8, + /// Peer chain genesis hash + genesis: H256, + /// Peer network id + network_id: u64, + /// Peer best block hash + latest_hash: H256, + /// Peer total difficulty if known + difficulty: Option, + /// Type of data currenty being requested from peer. + asking: PeerAsking, + /// A set of block numbers being requested + asking_blocks: Vec, + /// Holds requested header hash if currently requesting block header by hash + asking_hash: Option, + /// Holds requested snapshot chunk hash if any. + asking_snapshot_data: Option, + /// Request timestamp + ask_time: Instant, + /// Holds a set of transactions recently sent to this peer to avoid spamming. + last_sent_transactions: HashSet, + /// Pending request is expired and result should be ignored + expired: bool, + /// Peer fork confirmation status + confirmation: ForkConfirmation, + /// Best snapshot hash + snapshot_hash: Option, + /// Best snapshot block number + snapshot_number: Option, + /// Block set requested + block_set: Option, +} + +impl PeerInfo { + fn can_sync(&self) -> bool { + self.confirmation == ForkConfirmation::Confirmed && !self.expired + } + + fn is_allowed(&self) -> bool { + self.confirmation != ForkConfirmation::Unconfirmed && !self.expired + } + + fn reset_asking(&mut self) { + self.asking_blocks.clear(); + self.asking_hash = None; + // mark any pending requests as expired + if self.asking != PeerAsking::Nothing && self.is_allowed() { + self.expired = true; + } + } +} + +#[cfg(not(test))] +pub mod random { + use rand; + pub fn new() -> rand::ThreadRng { rand::thread_rng() } +} +#[cfg(test)] +pub mod random { + use rand::{self, SeedableRng}; + pub fn new() -> rand::XorShiftRng { rand::XorShiftRng::from_seed([0, 1, 2, 3]) } +} + +pub type RlpResponseResult = Result, PacketDecodeError>; +pub type Peers = HashMap; + +/// Blockchain sync handler. +/// See module documentation for more details. +pub struct ChainSync { + /// Sync state + state: SyncState, + /// Last block number for the start of sync + starting_block: BlockNumber, + /// Highest block number seen + highest_block: Option, + /// All connected peers + peers: Peers, + /// Peers active for current sync round + active_peers: HashSet, + /// Block download process for new blocks + new_blocks: BlockDownloader, + /// Block download process for ancient blocks + old_blocks: Option, + /// Last propagated block number + last_sent_block_number: BlockNumber, + /// Network ID + network_id: u64, + /// Optional fork block to check + fork_block: Option<(BlockNumber, H256)>, + /// Snapshot downloader. + snapshot: Snapshot, + /// Connected peers pending Status message. + /// Value is request timestamp. + handshaking_peers: HashMap, + /// Sync start timestamp. Measured when first peer is connected + sync_start_time: Option, + /// Transactions propagation statistics + transactions_stats: TransactionsStats, + /// Enable ancient block downloading + download_old_blocks: bool, + /// Shared private tx service. + private_tx_handler: Arc, + /// Enable warp sync. + warp_sync: WarpSync, +} + +impl ChainSync { + /// Create a new instance of syncing strategy. + pub fn new(config: SyncConfig, chain: &BlockChainClient, private_tx_handler: Arc) -> ChainSync { + let chain_info = chain.chain_info(); + let best_block = chain.chain_info().best_block_number; + let state = ChainSync::get_init_state(config.warp_sync, chain); + + let mut sync = ChainSync { + state, + starting_block: best_block, + highest_block: None, + peers: HashMap::new(), + handshaking_peers: HashMap::new(), + active_peers: HashSet::new(), + new_blocks: BlockDownloader::new(false, &chain_info.best_block_hash, chain_info.best_block_number), + old_blocks: None, + last_sent_block_number: 0, + network_id: config.network_id, + fork_block: config.fork_block, + download_old_blocks: config.download_old_blocks, + snapshot: Snapshot::new(), + sync_start_time: None, + transactions_stats: TransactionsStats::default(), + private_tx_handler, + warp_sync: config.warp_sync, + }; + sync.update_targets(chain); + sync + } + + fn get_init_state(warp_sync: WarpSync, chain: &BlockChainClient) -> SyncState { + let best_block = chain.chain_info().best_block_number; + match warp_sync { + WarpSync::Enabled => SyncState::WaitingPeers, + WarpSync::OnlyAndAfter(block) if block > best_block => SyncState::WaitingPeers, + _ => SyncState::Idle, + } + } + + /// Returns synchonization status + pub fn status(&self) -> SyncStatus { + let last_imported_number = self.new_blocks.last_imported_block_number(); + SyncStatus { + state: self.state.clone(), + protocol_version: ETH_PROTOCOL_VERSION_63.0, + network_id: self.network_id, + start_block_number: self.starting_block, + last_imported_block_number: Some(last_imported_number), + last_imported_old_block_number: self.old_blocks.as_ref().map(|d| d.last_imported_block_number()), + highest_block_number: self.highest_block.map(|n| cmp::max(n, last_imported_number)), + blocks_received: if last_imported_number > self.starting_block { last_imported_number - self.starting_block } else { 0 }, + blocks_total: match self.highest_block { Some(x) if x > self.starting_block => x - self.starting_block, _ => 0 }, + num_peers: self.peers.values().filter(|p| p.is_allowed()).count(), + num_active_peers: self.peers.values().filter(|p| p.is_allowed() && p.asking != PeerAsking::Nothing).count(), + num_snapshot_chunks: self.snapshot.total_chunks(), + snapshot_chunks_done: self.snapshot.done_chunks(), + mem_used: + self.new_blocks.heap_size() + + self.old_blocks.as_ref().map_or(0, |d| d.heap_size()) + + self.peers.heap_size_of_children(), + } + } + + /// Returns information on peers connections + pub fn peer_info(&self, peer_id: &PeerId) -> Option { + self.peers.get(peer_id).map(|peer_data| { + PeerInfoDigest { + version: peer_data.protocol_version as u32, + difficulty: peer_data.difficulty, + head: peer_data.latest_hash, + } + }) + } + + /// Returns transactions propagation statistics + pub fn transactions_stats(&self) -> &H256FastMap { + self.transactions_stats.stats() + } + + /// Updates transactions were received by a peer + pub fn transactions_received(&mut self, txs: &[UnverifiedTransaction], peer_id: PeerId) { + if let Some(peer_info) = self.peers.get_mut(&peer_id) { + peer_info.last_sent_transactions.extend(txs.iter().map(|tx| tx.hash())); + } + } + + /// Abort all sync activity + pub fn abort(&mut self, io: &mut SyncIo) { + self.reset_and_continue(io); + self.peers.clear(); + } + + /// Reset sync. Clear all downloaded data but keep the queue + fn reset(&mut self, io: &mut SyncIo) { + self.new_blocks.reset(); + let chain_info = io.chain().chain_info(); + for (_, ref mut p) in &mut self.peers { + if p.block_set != Some(BlockSet::OldBlocks) { + p.reset_asking(); + if p.difficulty.is_none() { + // assume peer has up to date difficulty + p.difficulty = Some(chain_info.pending_total_difficulty); + } + } + } + self.state = ChainSync::get_init_state(self.warp_sync, io.chain()); + // Reactivate peers only if some progress has been made + // since the last sync round of if starting fresh. + self.active_peers = self.peers.keys().cloned().collect(); + } + + /// Restart sync + pub fn reset_and_continue(&mut self, io: &mut SyncIo) { + trace!(target: "sync", "Restarting"); + if self.state == SyncState::SnapshotData { + debug!(target:"sync", "Aborting snapshot restore"); + io.snapshot_service().abort_restore(); + } + self.snapshot.clear(); + self.reset(io); + self.continue_sync(io); + } + + /// Remove peer from active peer set. Peer will be reactivated on the next sync + /// round. + fn deactivate_peer(&mut self, _io: &mut SyncIo, peer_id: PeerId) { + trace!(target: "sync", "Deactivating peer {}", peer_id); + self.active_peers.remove(&peer_id); + } + + fn maybe_start_snapshot_sync(&mut self, io: &mut SyncIo) { + if !self.warp_sync.is_enabled() || io.snapshot_service().supported_versions().is_none() { + trace!(target: "sync", "Skipping warp sync. Disabled or not supported."); + return; + } + if self.state != SyncState::WaitingPeers && self.state != SyncState::Blocks && self.state != SyncState::Waiting { + trace!(target: "sync", "Skipping warp sync. State: {:?}", self.state); + return; + } + // Make sure the snapshot block is not too far away from best block and network best block and + // that it is higher than fork detection block + let our_best_block = io.chain().chain_info().best_block_number; + let fork_block = self.fork_block.map_or(0, |(n, _)| n); + + let (best_hash, max_peers, snapshot_peers) = { + let expected_warp_block = match self.warp_sync { + WarpSync::OnlyAndAfter(block) => block, + _ => 0, + }; + //collect snapshot infos from peers + let snapshots = self.peers.iter() + .filter(|&(_, p)| p.is_allowed() && p.snapshot_number.map_or(false, |sn| + // Snapshot must be old enough that it's usefull to sync with it + our_best_block < sn && (sn - our_best_block) > SNAPSHOT_RESTORE_THRESHOLD && + // Snapshot must have been taken after the Fork + sn > fork_block && + // Snapshot must be greater than the warp barrier if any + sn > expected_warp_block && + // If we know a highest block, snapshot must be recent enough + self.highest_block.map_or(true, |highest| { + highest < sn || (highest - sn) <= SNAPSHOT_RESTORE_THRESHOLD + }) + )) + .filter_map(|(p, peer)| peer.snapshot_hash.map(|hash| (p, hash.clone()))) + .filter(|&(_, ref hash)| !self.snapshot.is_known_bad(hash)); + + let mut snapshot_peers = HashMap::new(); + let mut max_peers: usize = 0; + let mut best_hash = None; + for (p, hash) in snapshots { + let peers = snapshot_peers.entry(hash).or_insert_with(Vec::new); + peers.push(*p); + if peers.len() > max_peers { + max_peers = peers.len(); + best_hash = Some(hash); + } + } + (best_hash, max_peers, snapshot_peers) + }; + + let timeout = (self.state == SyncState::WaitingPeers) && self.sync_start_time.map_or(false, |t| t.elapsed() > WAIT_PEERS_TIMEOUT); + + if let (Some(hash), Some(peers)) = (best_hash, best_hash.map_or(None, |h| snapshot_peers.get(&h))) { + if max_peers >= SNAPSHOT_MIN_PEERS { + trace!(target: "sync", "Starting confirmed snapshot sync {:?} with {:?}", hash, peers); + self.start_snapshot_sync(io, peers); + } else if timeout { + trace!(target: "sync", "Starting unconfirmed snapshot sync {:?} with {:?}", hash, peers); + self.start_snapshot_sync(io, peers); + } + } else if timeout && !self.warp_sync.is_warp_only() { + trace!(target: "sync", "No snapshots found, starting full sync"); + self.state = SyncState::Idle; + self.continue_sync(io); + } + } + + fn start_snapshot_sync(&mut self, io: &mut SyncIo, peers: &[PeerId]) { + if !self.snapshot.have_manifest() { + for p in peers { + if self.peers.get(p).map_or(false, |p| p.asking == PeerAsking::Nothing) { + SyncRequester::request_snapshot_manifest(self, io, *p); + } + } + self.state = SyncState::SnapshotManifest; + trace!(target: "sync", "New snapshot sync with {:?}", peers); + } else { + self.state = SyncState::SnapshotData; + trace!(target: "sync", "Resumed snapshot sync with {:?}", peers); + } + } + + /// Restart sync disregarding the block queue status. May end up re-downloading up to QUEUE_SIZE blocks + pub fn restart(&mut self, io: &mut SyncIo) { + self.update_targets(io.chain()); + self.reset_and_continue(io); + } + + /// Update sync after the blockchain has been changed externally. + pub fn update_targets(&mut self, chain: &BlockChainClient) { + // Do not assume that the block queue/chain still has our last_imported_block + let chain = chain.chain_info(); + self.new_blocks = BlockDownloader::new(false, &chain.best_block_hash, chain.best_block_number); + self.old_blocks = None; + if self.download_old_blocks { + if let (Some(ancient_block_hash), Some(ancient_block_number)) = (chain.ancient_block_hash, chain.ancient_block_number) { + + trace!(target: "sync", "Downloading old blocks from {:?} (#{}) till {:?} (#{:?})", ancient_block_hash, ancient_block_number, chain.first_block_hash, chain.first_block_number); + let mut downloader = BlockDownloader::with_unlimited_reorg(true, &ancient_block_hash, ancient_block_number); + if let Some(hash) = chain.first_block_hash { + trace!(target: "sync", "Downloader target set to {:?}", hash); + downloader.set_target(&hash); + } + self.old_blocks = Some(downloader); + } + } + } + + /// Resume downloading + fn continue_sync(&mut self, io: &mut SyncIo) { + // Collect active peers that can sync + let confirmed_peers: Vec<(PeerId, u8)> = self.peers.iter().filter_map(|(peer_id, peer)| + if peer.can_sync() { + Some((*peer_id, peer.protocol_version)) + } else { + None + } + ).collect(); + let mut peers: Vec<(PeerId, u8)> = confirmed_peers.iter().filter(|&&(peer_id, _)| + self.active_peers.contains(&peer_id) + ).map(|v| *v).collect(); + + random::new().shuffle(&mut peers); //TODO: sort by rating + // prefer peers with higher protocol version + peers.sort_by(|&(_, ref v1), &(_, ref v2)| v1.cmp(v2)); + trace!( + target: "sync", + "Syncing with peers: {} active, {} confirmed, {} total", + self.active_peers.len(), confirmed_peers.len(), self.peers.len() + ); + for (peer_id, _) in peers { + self.sync_peer(io, peer_id, false); + } + + if + (self.state == SyncState::Blocks || self.state == SyncState::NewBlocks) && + !self.peers.values().any(|p| p.asking != PeerAsking::Nothing && p.block_set != Some(BlockSet::OldBlocks) && p.can_sync()) + { + self.complete_sync(io); + } + } + + /// Called after all blocks have been downloaded + fn complete_sync(&mut self, io: &mut SyncIo) { + trace!(target: "sync", "Sync complete"); + self.reset(io); + } + + /// Enter waiting state + fn pause_sync(&mut self) { + trace!(target: "sync", "Block queue full, pausing sync"); + self.state = SyncState::Waiting; + } + + /// Find something to do for a peer. Called for a new peer or when a peer is done with its task. + fn sync_peer(&mut self, io: &mut SyncIo, peer_id: PeerId, force: bool) { + if !self.active_peers.contains(&peer_id) { + trace!(target: "sync", "Skipping deactivated peer {}", peer_id); + return; + } + let (peer_latest, peer_difficulty, peer_snapshot_number, peer_snapshot_hash) = { + if let Some(peer) = self.peers.get_mut(&peer_id) { + if peer.asking != PeerAsking::Nothing || !peer.can_sync() { + trace!(target: "sync", "Skipping busy peer {}", peer_id); + return; + } + if self.state == SyncState::Waiting { + trace!(target: "sync", "Waiting for the block queue"); + return; + } + if self.state == SyncState::SnapshotWaiting { + trace!(target: "sync", "Waiting for the snapshot restoration"); + return; + } + (peer.latest_hash.clone(), peer.difficulty.clone(), peer.snapshot_number.as_ref().cloned().unwrap_or(0), peer.snapshot_hash.as_ref().cloned()) + } else { + return; + } + }; + let chain_info = io.chain().chain_info(); + let syncing_difficulty = chain_info.pending_total_difficulty; + let num_active_peers = self.peers.values().filter(|p| p.asking != PeerAsking::Nothing).count(); + + let higher_difficulty = peer_difficulty.map_or(true, |pd| pd > syncing_difficulty); + if force || higher_difficulty || self.old_blocks.is_some() { + match self.state { + SyncState::WaitingPeers => { + trace!( + target: "sync", + "Checking snapshot sync: {} vs {} (peer: {})", + peer_snapshot_number, + chain_info.best_block_number, + peer_id + ); + self.maybe_start_snapshot_sync(io); + }, + SyncState::Idle | SyncState::Blocks | SyncState::NewBlocks => { + if io.chain().queue_info().is_full() { + self.pause_sync(); + return; + } + + let have_latest = io.chain().block_status(BlockId::Hash(peer_latest)) != BlockStatus::Unknown; + trace!(target: "sync", "Considering peer {}, force={}, td={:?}, our td={}, latest={}, have_latest={}, state={:?}", peer_id, force, peer_difficulty, syncing_difficulty, peer_latest, have_latest, self.state); + if !have_latest && (higher_difficulty || force || self.state == SyncState::NewBlocks) { + // check if got new blocks to download + trace!(target: "sync", "Syncing with peer {}, force={}, td={:?}, our td={}, state={:?}", peer_id, force, peer_difficulty, syncing_difficulty, self.state); + if let Some(request) = self.new_blocks.request_blocks(io, num_active_peers) { + SyncRequester::request_blocks(self, io, peer_id, request, BlockSet::NewBlocks); + if self.state == SyncState::Idle { + self.state = SyncState::Blocks; + } + return; + } + } + + // Only ask for old blocks if the peer has a higher difficulty + if force || higher_difficulty { + if let Some(request) = self.old_blocks.as_mut().and_then(|d| d.request_blocks(io, num_active_peers)) { + SyncRequester::request_blocks(self, io, peer_id, request, BlockSet::OldBlocks); + return; + } + } else { + trace!(target: "sync", "peer {} is not suitable for asking old blocks", peer_id); + self.deactivate_peer(io, peer_id); + } + }, + SyncState::SnapshotData => { + match io.snapshot_service().status() { + RestorationStatus::Ongoing { state_chunks_done, block_chunks_done, .. } => { + // Initialize the snapshot if not already done + self.snapshot.initialize(io.snapshot_service()); + if self.snapshot.done_chunks() - (state_chunks_done + block_chunks_done) as usize > MAX_SNAPSHOT_CHUNKS_DOWNLOAD_AHEAD { + trace!(target: "sync", "Snapshot queue full, pausing sync"); + self.state = SyncState::SnapshotWaiting; + return; + } + }, + RestorationStatus::Initializing { .. } => { + trace!(target: "warp", "Snapshot is stil initializing."); + return; + }, + _ => { + return; + }, + } + + if peer_snapshot_hash.is_some() && peer_snapshot_hash == self.snapshot.snapshot_hash() { + self.clear_peer_download(peer_id); + SyncRequester::request_snapshot_data(self, io, peer_id); + } + }, + SyncState::SnapshotManifest | //already downloading from other peer + SyncState::Waiting | + SyncState::SnapshotWaiting => () + } + } else { + trace!(target: "sync", "Skipping peer {}, force={}, td={:?}, our td={}, state={:?}", peer_id, force, peer_difficulty, syncing_difficulty, self.state); + } + } + + /// Clear all blocks/headers marked as being downloaded by a peer. + fn clear_peer_download(&mut self, peer_id: PeerId) { + if let Some(ref peer) = self.peers.get(&peer_id) { + match peer.asking { + PeerAsking::BlockHeaders => { + if let Some(ref hash) = peer.asking_hash { + self.new_blocks.clear_header_download(hash); + if let Some(ref mut old) = self.old_blocks { + old.clear_header_download(hash); + } + } + }, + PeerAsking::BlockBodies => { + self.new_blocks.clear_body_download(&peer.asking_blocks); + if let Some(ref mut old) = self.old_blocks { + old.clear_body_download(&peer.asking_blocks); + } + }, + PeerAsking::BlockReceipts => { + self.new_blocks.clear_receipt_download(&peer.asking_blocks); + if let Some(ref mut old) = self.old_blocks { + old.clear_receipt_download(&peer.asking_blocks); + } + }, + PeerAsking::SnapshotData => { + if let Some(hash) = peer.asking_snapshot_data { + self.snapshot.clear_chunk_download(&hash); + } + }, + _ => (), + } + } + } + + /// Checks if there are blocks fully downloaded that can be imported into the blockchain and does the import. + fn collect_blocks(&mut self, io: &mut SyncIo, block_set: BlockSet) { + match block_set { + BlockSet::NewBlocks => { + if self.new_blocks.collect_blocks(io, self.state == SyncState::NewBlocks) == Err(DownloaderImportError::Invalid) { + self.restart(io); + } + }, + BlockSet::OldBlocks => { + if self.old_blocks.as_mut().map_or(false, |downloader| { downloader.collect_blocks(io, false) == Err(DownloaderImportError::Invalid) }) { + self.restart(io); + } else if self.old_blocks.as_ref().map_or(false, |downloader| { downloader.is_complete() }) { + trace!(target: "sync", "Background block download is complete"); + self.old_blocks = None; + } + } + } + } + + /// Reset peer status after request is complete. + fn reset_peer_asking(&mut self, peer_id: PeerId, asking: PeerAsking) -> bool { + if let Some(ref mut peer) = self.peers.get_mut(&peer_id) { + peer.expired = false; + peer.block_set = None; + if peer.asking != asking { + trace!(target:"sync", "Asking {:?} while expected {:?}", peer.asking, asking); + peer.asking = PeerAsking::Nothing; + return false; + } else { + peer.asking = PeerAsking::Nothing; + return true; + } + } + false + } + + /// Send Status message + fn send_status(&mut self, io: &mut SyncIo, peer: PeerId) -> Result<(), network::Error> { + let warp_protocol_version = io.protocol_version(&WARP_SYNC_PROTOCOL_ID, peer); + let warp_protocol = warp_protocol_version != 0; + let protocol = if warp_protocol { warp_protocol_version } else { ETH_PROTOCOL_VERSION_63.0 }; + trace!(target: "sync", "Sending status to {}, protocol version {}", peer, protocol); + let mut packet = RlpStream::new_list(if warp_protocol { 7 } else { 5 }); + let chain = io.chain().chain_info(); + packet.append(&(protocol as u32)); + packet.append(&self.network_id); + packet.append(&chain.total_difficulty); + packet.append(&chain.best_block_hash); + packet.append(&chain.genesis_hash); + if warp_protocol { + let manifest = io.snapshot_service().manifest(); + let block_number = manifest.as_ref().map_or(0, |m| m.block_number); + let manifest_hash = manifest.map_or(H256::new(), |m| keccak(m.into_rlp())); + packet.append(&manifest_hash); + packet.append(&block_number); + } + io.respond(STATUS_PACKET, packet.out()) + } + + pub fn maintain_peers(&mut self, io: &mut SyncIo) { + let tick = Instant::now(); + let mut aborting = Vec::new(); + for (peer_id, peer) in &self.peers { + let elapsed = tick - peer.ask_time; + let timeout = match peer.asking { + PeerAsking::BlockHeaders => elapsed > HEADERS_TIMEOUT, + PeerAsking::BlockBodies => elapsed > BODIES_TIMEOUT, + PeerAsking::BlockReceipts => elapsed > RECEIPTS_TIMEOUT, + PeerAsking::Nothing => false, + PeerAsking::ForkHeader => elapsed > FORK_HEADER_TIMEOUT, + PeerAsking::SnapshotManifest => elapsed > SNAPSHOT_MANIFEST_TIMEOUT, + PeerAsking::SnapshotData => elapsed > SNAPSHOT_DATA_TIMEOUT, + }; + if timeout { + debug!(target:"sync", "Timeout {}", peer_id); + io.disconnect_peer(*peer_id); + aborting.push(*peer_id); + } + } + for p in aborting { + SyncHandler::on_peer_aborting(self, io, p); + } + + // Check for handshake timeouts + for (peer, &ask_time) in &self.handshaking_peers { + let elapsed = (tick - ask_time) / 1_000_000_000; + if elapsed > STATUS_TIMEOUT { + trace!(target:"sync", "Status timeout {}", peer); + io.disconnect_peer(*peer); + } + } + } + + fn check_resume(&mut self, io: &mut SyncIo) { + match self.state { + SyncState::Waiting if !io.chain().queue_info().is_full() => { + self.state = SyncState::Blocks; + self.continue_sync(io); + }, + SyncState::SnapshotWaiting => { + match io.snapshot_service().status() { + RestorationStatus::Inactive => { + trace!(target:"sync", "Snapshot restoration is complete"); + self.restart(io); + }, + RestorationStatus::Initializing { .. } => { + trace!(target:"sync", "Snapshot restoration is initializing"); + }, + RestorationStatus::Ongoing { state_chunks_done, block_chunks_done, .. } => { + if !self.snapshot.is_complete() && self.snapshot.done_chunks() - (state_chunks_done + block_chunks_done) as usize <= MAX_SNAPSHOT_CHUNKS_DOWNLOAD_AHEAD { + trace!(target:"sync", "Resuming snapshot sync"); + self.state = SyncState::SnapshotData; + self.continue_sync(io); + } + }, + RestorationStatus::Failed => { + trace!(target: "sync", "Snapshot restoration aborted"); + self.state = SyncState::WaitingPeers; + self.snapshot.clear(); + self.continue_sync(io); + }, + } + }, + _ => (), + } + } + + /// creates rlp to send for the tree defined by 'from' and 'to' hashes + fn create_new_hashes_rlp(chain: &BlockChainClient, from: &H256, to: &H256) -> Option { + match chain.tree_route(from, to) { + Some(route) => { + let uncles = chain.find_uncles(from).unwrap_or_else(Vec::new); + match route.blocks.len() { + 0 => None, + _ => { + let mut blocks = route.blocks; + blocks.extend(uncles); + let mut rlp_stream = RlpStream::new_list(blocks.len()); + for block_hash in blocks { + let mut hash_rlp = RlpStream::new_list(2); + let number = chain.block_header(BlockId::Hash(block_hash.clone())) + .expect("chain.tree_route and chain.find_uncles only return hahses of blocks that are in the blockchain. qed.").number(); + hash_rlp.append(&block_hash); + hash_rlp.append(&number); + rlp_stream.append_raw(hash_rlp.as_raw(), 1); + } + Some(rlp_stream.out()) + } + } + }, + None => None + } + } + + /// creates rlp from block bytes and total difficulty + fn create_block_rlp(bytes: &Bytes, total_difficulty: U256) -> Bytes { + let mut rlp_stream = RlpStream::new_list(2); + rlp_stream.append_raw(bytes, 1); + rlp_stream.append(&total_difficulty); + rlp_stream.out() + } + + /// creates latest block rlp for the given client + fn create_latest_block_rlp(chain: &BlockChainClient) -> Bytes { + ChainSync::create_block_rlp( + &chain.block(BlockId::Hash(chain.chain_info().best_block_hash)) + .expect("Best block always exists").into_inner(), + chain.chain_info().total_difficulty + ) + } + + /// creates given hash block rlp for the given client + fn create_new_block_rlp(chain: &BlockChainClient, hash: &H256) -> Bytes { + ChainSync::create_block_rlp( + &chain.block(BlockId::Hash(hash.clone())).expect("Block has just been sealed; qed").into_inner(), + chain.block_total_difficulty(BlockId::Hash(hash.clone())).expect("Block has just been sealed; qed.") + ) + } + + /// returns peer ids that have different blocks than our chain + fn get_lagging_peers(&mut self, chain_info: &BlockChainInfo) -> Vec { + let latest_hash = chain_info.best_block_hash; + self + .peers + .iter_mut() + .filter_map(|(&id, ref mut peer_info)| { + trace!(target: "sync", "Checking peer our best {} their best {}", latest_hash, peer_info.latest_hash); + if peer_info.latest_hash != latest_hash { + Some(id) + } else { + None + } + }) + .collect::>() + } + + fn select_random_peers(peers: &[PeerId]) -> Vec { + // take sqrt(x) peers + let mut peers = peers.to_vec(); + let mut count = (peers.len() as f64).powf(0.5).round() as usize; + count = cmp::min(count, MAX_PEERS_PROPAGATION); + count = cmp::max(count, MIN_PEERS_PROPAGATION); + random::new().shuffle(&mut peers); + peers.truncate(count); + peers + } + + fn get_consensus_peers(&self) -> Vec { + self.peers.iter().filter_map(|(id, p)| if p.protocol_version >= PAR_PROTOCOL_VERSION_2.0 { Some(*id) } else { None }).collect() + } + + fn get_private_transaction_peers(&self) -> Vec { + self.peers.iter().filter_map(|(id, p)| if p.protocol_version >= PAR_PROTOCOL_VERSION_3.0 { Some(*id) } else { None }).collect() + } + + /// Maintain other peers. Send out any new blocks and transactions + pub fn maintain_sync(&mut self, io: &mut SyncIo) { + self.maybe_start_snapshot_sync(io); + self.check_resume(io); + } + + /// called when block is imported to chain - propagates the blocks and updates transactions sent to peers + pub fn chain_new_blocks(&mut self, io: &mut SyncIo, _imported: &[H256], invalid: &[H256], enacted: &[H256], _retracted: &[H256], sealed: &[H256], proposed: &[Bytes]) { + let queue_info = io.chain().queue_info(); + let is_syncing = self.status().is_syncing(queue_info); + + if !is_syncing || !sealed.is_empty() || !proposed.is_empty() { + trace!(target: "sync", "Propagating blocks, state={:?}", self.state); + SyncPropagator::propagate_latest_blocks(self, io, sealed); + SyncPropagator::propagate_proposed_blocks(self, io, proposed); + } + if !invalid.is_empty() { + trace!(target: "sync", "Bad blocks in the queue, restarting"); + self.restart(io); + } + + if !is_syncing && !enacted.is_empty() && !self.peers.is_empty() { + // Select random peer to re-broadcast transactions to. + let peer = random::new().gen_range(0, self.peers.len()); + trace!(target: "sync", "Re-broadcasting transactions to a random peer."); + self.peers.values_mut().nth(peer).map(|peer_info| + peer_info.last_sent_transactions.clear() + ); + } + } + + /// Dispatch incoming requests and responses + pub fn dispatch_packet(sync: &RwLock, io: &mut SyncIo, peer: PeerId, packet_id: u8, data: &[u8]) { + SyncSupplier::dispatch_packet(sync, io, peer, packet_id, data) + } + + pub fn on_packet(&mut self, io: &mut SyncIo, peer: PeerId, packet_id: u8, data: &[u8]) { + debug!(target: "sync", "{} -> Dispatching packet: {}", peer, packet_id); + SyncHandler::on_packet(self, io, peer, packet_id, data); + } + + /// Called when peer sends us new consensus packet + pub fn on_consensus_packet(io: &mut SyncIo, peer_id: PeerId, r: &Rlp) -> Result<(), PacketDecodeError> { + SyncHandler::on_consensus_packet(io, peer_id, r) + } + + /// Called by peer when it is disconnecting + pub fn on_peer_aborting(&mut self, io: &mut SyncIo, peer: PeerId) { + SyncHandler::on_peer_aborting(self, io, peer); + } + + /// Called when a new peer is connected + pub fn on_peer_connected(&mut self, io: &mut SyncIo, peer: PeerId) { + SyncHandler::on_peer_connected(self, io, peer); + } + + /// propagates new transactions to all peers + pub fn propagate_new_transactions(&mut self, io: &mut SyncIo) -> usize { + SyncPropagator::propagate_new_transactions(self, io) + } + + /// Broadcast consensus message to peers. + pub fn propagate_consensus_packet(&mut self, io: &mut SyncIo, packet: Bytes) { + SyncPropagator::propagate_consensus_packet(self, io, packet); + } + + /// Broadcast private transaction message to peers. + pub fn propagate_private_transaction(&mut self, io: &mut SyncIo, packet: Bytes) { + SyncPropagator::propagate_private_transaction(self, io, packet); + } + + /// Broadcast signed private transaction message to peers. + pub fn propagate_signed_private_transaction(&mut self, io: &mut SyncIo, packet: Bytes) { + SyncPropagator::propagate_signed_private_transaction(self, io, packet); + } +} + +#[cfg(test)] +pub mod tests { + use std::collections::{HashSet, VecDeque}; + use ethkey; + use network::PeerId; + use tests::helpers::{TestIo}; + use tests::snapshot::TestSnapshotService; + use ethereum_types::{H256, U256, Address}; + use parking_lot::RwLock; + use bytes::Bytes; + use rlp::{Rlp, RlpStream}; + use super::*; + use ::SyncConfig; + use super::{PeerInfo, PeerAsking}; + use ethcore::header::*; + use ethcore::client::{BlockChainClient, EachBlockWith, TestBlockChainClient, ChainInfo, BlockInfo}; + use ethcore::miner::{MinerService, PendingOrdering}; + use private_tx::NoopPrivateTxHandler; + + pub fn get_dummy_block(order: u32, parent_hash: H256) -> Bytes { + let mut header = Header::new(); + header.set_gas_limit(0.into()); + header.set_difficulty((order * 100).into()); + header.set_timestamp((order * 10) as u64); + header.set_number(order as u64); + header.set_parent_hash(parent_hash); + header.set_state_root(H256::zero()); + + let mut rlp = RlpStream::new_list(3); + rlp.append(&header); + rlp.append_raw(&::rlp::EMPTY_LIST_RLP, 1); + rlp.append_raw(&::rlp::EMPTY_LIST_RLP, 1); + rlp.out() + } + + pub fn get_dummy_blocks(order: u32, parent_hash: H256) -> Bytes { + let mut rlp = RlpStream::new_list(1); + rlp.append_raw(&get_dummy_block(order, parent_hash), 1); + let difficulty: U256 = (100 * order).into(); + rlp.append(&difficulty); + rlp.out() + } + + pub fn get_dummy_hashes() -> Bytes { + let mut rlp = RlpStream::new_list(5); + for _ in 0..5 { + let mut hash_d_rlp = RlpStream::new_list(2); + let hash: H256 = H256::from(0u64); + let diff: U256 = U256::from(1u64); + hash_d_rlp.append(&hash); + hash_d_rlp.append(&diff); + + rlp.append_raw(&hash_d_rlp.out(), 1); + } + + rlp.out() + } + + fn queue_info(unverified: usize, verified: usize) -> BlockQueueInfo { + BlockQueueInfo { + unverified_queue_size: unverified, + verified_queue_size: verified, + verifying_queue_size: 0, + max_queue_size: 1000, + max_mem_use: 1000, + mem_used: 500 + } + } + + fn sync_status(state: SyncState) -> SyncStatus { + SyncStatus { + state: state, + protocol_version: 0, + network_id: 0, + start_block_number: 0, + last_imported_block_number: None, + highest_block_number: None, + blocks_total: 0, + blocks_received: 0, + num_peers: 0, + num_active_peers: 0, + mem_used: 0, + num_snapshot_chunks: 0, + snapshot_chunks_done: 0, + last_imported_old_block_number: None, + } + } + + #[test] + fn is_still_verifying() { + assert!(!sync_status(SyncState::Idle).is_syncing(queue_info(2, 1))); + assert!(sync_status(SyncState::Idle).is_syncing(queue_info(2, 2))); + } + + #[test] + fn is_synced_state() { + assert!(sync_status(SyncState::Blocks).is_syncing(queue_info(0, 0))); + assert!(!sync_status(SyncState::Idle).is_syncing(queue_info(0, 0))); + } + + pub fn dummy_sync_with_peer(peer_latest_hash: H256, client: &BlockChainClient) -> ChainSync { + let mut sync = ChainSync::new(SyncConfig::default(), client, Arc::new(NoopPrivateTxHandler)); + insert_dummy_peer(&mut sync, 0, peer_latest_hash); + sync + } + + pub fn insert_dummy_peer(sync: &mut ChainSync, peer_id: PeerId, peer_latest_hash: H256) { + sync.peers.insert(peer_id, + PeerInfo { + protocol_version: 0, + genesis: H256::zero(), + network_id: 0, + latest_hash: peer_latest_hash, + difficulty: None, + asking: PeerAsking::Nothing, + asking_blocks: Vec::new(), + asking_hash: None, + ask_time: Instant::now(), + last_sent_transactions: HashSet::new(), + expired: false, + confirmation: super::ForkConfirmation::Confirmed, + snapshot_number: None, + snapshot_hash: None, + asking_snapshot_data: None, + block_set: None, + }); + + } + + #[test] + fn finds_lagging_peers() { + let mut client = TestBlockChainClient::new(); + client.add_blocks(100, EachBlockWith::Uncle); + let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(10), &client); + let chain_info = client.chain_info(); + + let lagging_peers = sync.get_lagging_peers(&chain_info); + + assert_eq!(1, lagging_peers.len()); + } + + #[test] + fn calculates_tree_for_lagging_peer() { + let mut client = TestBlockChainClient::new(); + client.add_blocks(15, EachBlockWith::Uncle); + + let start = client.block_hash_delta_minus(4); + let end = client.block_hash_delta_minus(2); + + // wrong way end -> start, should be None + let rlp = ChainSync::create_new_hashes_rlp(&client, &end, &start); + assert!(rlp.is_none()); + + let rlp = ChainSync::create_new_hashes_rlp(&client, &start, &end).unwrap(); + // size of three rlp encoded hash-difficulty + assert_eq!(107, rlp.len()); + } + // idea is that what we produce when propagading latest hashes should be accepted in + // on_peer_new_hashes in our code as well + #[test] + fn hashes_rlp_mutually_acceptable() { + let mut client = TestBlockChainClient::new(); + client.add_blocks(100, EachBlockWith::Uncle); + let queue = RwLock::new(VecDeque::new()); + let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); + let chain_info = client.chain_info(); + let ss = TestSnapshotService::new(); + let mut io = TestIo::new(&mut client, &ss, &queue, None); + + let peers = sync.get_lagging_peers(&chain_info); + SyncPropagator::propagate_new_hashes(&mut sync, &chain_info, &mut io, &peers); + + let data = &io.packets[0].data.clone(); + let result = SyncHandler::on_peer_new_hashes(&mut sync, &mut io, 0, &Rlp::new(data)); + assert!(result.is_ok()); + } + + // idea is that what we produce when propagading latest block should be accepted in + // on_peer_new_block in our code as well + #[test] + fn block_rlp_mutually_acceptable() { + let mut client = TestBlockChainClient::new(); + client.add_blocks(100, EachBlockWith::Uncle); + let queue = RwLock::new(VecDeque::new()); + let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); + let chain_info = client.chain_info(); + let ss = TestSnapshotService::new(); + let mut io = TestIo::new(&mut client, &ss, &queue, None); + + let peers = sync.get_lagging_peers(&chain_info); + SyncPropagator::propagate_blocks(&mut sync, &chain_info, &mut io, &[], &peers); + + let data = &io.packets[0].data.clone(); + let result = SyncHandler::on_peer_new_block(&mut sync, &mut io, 0, &Rlp::new(data)); + assert!(result.is_ok()); + } + + #[test] + fn should_add_transactions_to_queue() { + fn sender(tx: &UnverifiedTransaction) -> Address { + ethkey::public_to_address(&tx.recover_public().unwrap()) + } + + // given + let mut client = TestBlockChainClient::new(); + client.add_blocks(98, EachBlockWith::Uncle); + client.add_blocks(1, EachBlockWith::UncleAndTransaction); + client.add_blocks(1, EachBlockWith::Transaction); + let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); + + let good_blocks = vec![client.block_hash_delta_minus(2)]; + let retracted_blocks = vec![client.block_hash_delta_minus(1)]; + + // Add some balance to clients and reset nonces + for h in &[good_blocks[0], retracted_blocks[0]] { + let block = client.block(BlockId::Hash(*h)).unwrap(); + let sender = sender(&block.transactions()[0]);; + client.set_balance(sender, U256::from(10_000_000_000_000_000_000u64)); + client.set_nonce(sender, U256::from(0)); + } + + // when + { + let queue = RwLock::new(VecDeque::new()); + let ss = TestSnapshotService::new(); + let mut io = TestIo::new(&mut client, &ss, &queue, None); + io.chain.miner.chain_new_blocks(io.chain, &[], &[], &[], &good_blocks, false); + sync.chain_new_blocks(&mut io, &[], &[], &[], &good_blocks, &[], &[]); + assert_eq!(io.chain.miner.ready_transactions(io.chain, 10, PendingOrdering::Priority).len(), 1); + } + // We need to update nonce status (because we say that the block has been imported) + for h in &[good_blocks[0]] { + let block = client.block(BlockId::Hash(*h)).unwrap(); + client.set_nonce(sender(&block.transactions()[0]), U256::from(1)); + } + { + let queue = RwLock::new(VecDeque::new()); + let ss = TestSnapshotService::new(); + let mut io = TestIo::new(&client, &ss, &queue, None); + io.chain.miner.chain_new_blocks(io.chain, &[], &[], &good_blocks, &retracted_blocks, false); + sync.chain_new_blocks(&mut io, &[], &[], &good_blocks, &retracted_blocks, &[], &[]); + } + + // then + assert_eq!(client.miner.ready_transactions(&client, 10, PendingOrdering::Priority).len(), 1); + } + + #[test] + fn should_not_add_transactions_to_queue_if_not_synced() { + // given + let mut client = TestBlockChainClient::new(); + client.add_blocks(98, EachBlockWith::Uncle); + client.add_blocks(1, EachBlockWith::UncleAndTransaction); + client.add_blocks(1, EachBlockWith::Transaction); + let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); + + let good_blocks = vec![client.block_hash_delta_minus(2)]; + let retracted_blocks = vec![client.block_hash_delta_minus(1)]; + + let queue = RwLock::new(VecDeque::new()); + let ss = TestSnapshotService::new(); + let mut io = TestIo::new(&mut client, &ss, &queue, None); + + // when + sync.chain_new_blocks(&mut io, &[], &[], &[], &good_blocks, &[], &[]); + assert_eq!(io.chain.miner.queue_status().status.transaction_count, 0); + sync.chain_new_blocks(&mut io, &[], &[], &good_blocks, &retracted_blocks, &[], &[]); + + // then + let status = io.chain.miner.queue_status(); + assert_eq!(status.status.transaction_count, 0); + } +} diff --git a/ethcore/sync/src/chain/propagator.rs b/ethcore/sync/src/chain/propagator.rs new file mode 100644 index 0000000000000000000000000000000000000000..75cf550f28bfa50fefb2de225572cd99a3aa7634 --- /dev/null +++ b/ethcore/sync/src/chain/propagator.rs @@ -0,0 +1,637 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use bytes::Bytes; +use ethereum_types::H256; +use ethcore::client::BlockChainInfo; +use ethcore::header::BlockNumber; +use network::{PeerId, PacketId}; +use rand::Rng; +use rlp::{Encodable, RlpStream}; +use sync_io::SyncIo; +use std::cmp; +use std::collections::HashSet; +use transaction::SignedTransaction; + +use super::{ + random, + ChainSync, + MAX_PEER_LAG_PROPAGATION, + MAX_PEERS_PROPAGATION, + MAX_TRANSACTION_PACKET_SIZE, + MAX_TRANSACTIONS_TO_PROPAGATE, + MAX_TRANSACTIONS_TO_QUERY, + MIN_PEERS_PROPAGATION, + CONSENSUS_DATA_PACKET, + NEW_BLOCK_HASHES_PACKET, + NEW_BLOCK_PACKET, + PRIVATE_TRANSACTION_PACKET, + SIGNED_PRIVATE_TRANSACTION_PACKET, + TRANSACTIONS_PACKET, +}; + +/// Checks if peer is able to process service transactions +fn accepts_service_transaction(client_id: &str) -> bool { + // Parity versions starting from this will accept service-transactions + const SERVICE_TRANSACTIONS_VERSION: (u32, u32) = (1u32, 6u32); + // Parity client string prefix + const PARITY_CLIENT_ID_PREFIX: &'static str = "Parity/v"; + + if !client_id.starts_with(PARITY_CLIENT_ID_PREFIX) { + return false; + } + let ver: Vec = client_id[PARITY_CLIENT_ID_PREFIX.len()..].split('.') + .take(2) + .filter_map(|s| s.parse().ok()) + .collect(); + ver.len() == 2 && (ver[0] > SERVICE_TRANSACTIONS_VERSION.0 || (ver[0] == SERVICE_TRANSACTIONS_VERSION.0 && ver[1] >= SERVICE_TRANSACTIONS_VERSION.1)) +} + +/// The Chain Sync Propagator: propagates data to peers +pub struct SyncPropagator; + +impl SyncPropagator { + /// propagates latest block to a set of peers + pub fn propagate_blocks(sync: &mut ChainSync, chain_info: &BlockChainInfo, io: &mut SyncIo, blocks: &[H256], peers: &[PeerId]) -> usize { + trace!(target: "sync", "Sending NewBlocks to {:?}", peers); + let mut sent = 0; + for peer_id in peers { + if blocks.is_empty() { + let rlp = ChainSync::create_latest_block_rlp(io.chain()); + SyncPropagator::send_packet(io, *peer_id, NEW_BLOCK_PACKET, rlp); + } else { + for h in blocks { + let rlp = ChainSync::create_new_block_rlp(io.chain(), h); + SyncPropagator::send_packet(io, *peer_id, NEW_BLOCK_PACKET, rlp); + } + } + if let Some(ref mut peer) = sync.peers.get_mut(peer_id) { + peer.latest_hash = chain_info.best_block_hash.clone(); + } + sent += 1; + } + sent + } + + /// propagates new known hashes to all peers + pub fn propagate_new_hashes(sync: &mut ChainSync, chain_info: &BlockChainInfo, io: &mut SyncIo, peers: &[PeerId]) -> usize { + trace!(target: "sync", "Sending NewHashes to {:?}", peers); + let mut sent = 0; + let last_parent = *io.chain().best_block_header().parent_hash(); + for peer_id in peers { + sent += match ChainSync::create_new_hashes_rlp(io.chain(), &last_parent, &chain_info.best_block_hash) { + Some(rlp) => { + { + if let Some(ref mut peer) = sync.peers.get_mut(peer_id) { + peer.latest_hash = chain_info.best_block_hash.clone(); + } + } + SyncPropagator::send_packet(io, *peer_id, NEW_BLOCK_HASHES_PACKET, rlp); + 1 + }, + None => 0 + } + } + sent + } + + /// propagates new transactions to all peers + pub fn propagate_new_transactions(sync: &mut ChainSync, io: &mut SyncIo) -> usize { + // Early out if nobody to send to. + if sync.peers.is_empty() { + return 0; + } + + let transactions = io.chain().ready_transactions(MAX_TRANSACTIONS_TO_QUERY); + if transactions.is_empty() { + return 0; + } + + let (transactions, service_transactions): (Vec<_>, Vec<_>) = transactions.iter() + .map(|tx| tx.signed()) + .partition(|tx| !tx.gas_price.is_zero()); + + // usual transactions could be propagated to all peers + let mut affected_peers = HashSet::new(); + if !transactions.is_empty() { + let peers = SyncPropagator::select_peers_for_transactions(sync, |_| true); + affected_peers = SyncPropagator::propagate_transactions_to_peers(sync, io, peers, transactions); + } + + // most of times service_transactions will be empty + // => there's no need to merge packets + if !service_transactions.is_empty() { + let service_transactions_peers = SyncPropagator::select_peers_for_transactions(sync, |peer_id| accepts_service_transaction(&io.peer_info(*peer_id))); + let service_transactions_affected_peers = SyncPropagator::propagate_transactions_to_peers(sync, io, service_transactions_peers, service_transactions); + affected_peers.extend(&service_transactions_affected_peers); + } + + affected_peers.len() + } + + fn propagate_transactions_to_peers(sync: &mut ChainSync, io: &mut SyncIo, peers: Vec, transactions: Vec<&SignedTransaction>) -> HashSet { + let all_transactions_hashes = transactions.iter() + .map(|tx| tx.hash()) + .collect::>(); + let all_transactions_rlp = { + let mut packet = RlpStream::new_list(transactions.len()); + for tx in &transactions { packet.append(&**tx); } + packet.out() + }; + + // Clear old transactions from stats + sync.transactions_stats.retain(&all_transactions_hashes); + + // sqrt(x)/x scaled to max u32 + let block_number = io.chain().chain_info().best_block_number; + + let lucky_peers = { + peers.into_iter() + .filter_map(|peer_id| { + let stats = &mut sync.transactions_stats; + let peer_info = sync.peers.get_mut(&peer_id) + .expect("peer_id is form peers; peers is result of select_peers_for_transactions; select_peers_for_transactions selects peers from self.peers; qed"); + + // Send all transactions + if peer_info.last_sent_transactions.is_empty() { + // update stats + for hash in &all_transactions_hashes { + let id = io.peer_session_info(peer_id).and_then(|info| info.id); + stats.propagated(hash, id, block_number); + } + peer_info.last_sent_transactions = all_transactions_hashes.clone(); + return Some((peer_id, all_transactions_hashes.len(), all_transactions_rlp.clone())); + } + + // Get hashes of all transactions to send to this peer + let to_send = all_transactions_hashes.difference(&peer_info.last_sent_transactions) + .take(MAX_TRANSACTIONS_TO_PROPAGATE) + .cloned() + .collect::>(); + if to_send.is_empty() { + return None; + } + + // Construct RLP + let (packet, to_send) = { + let mut to_send = to_send; + let mut packet = RlpStream::new(); + packet.begin_unbounded_list(); + let mut pushed = 0; + for tx in &transactions { + let hash = tx.hash(); + if to_send.contains(&hash) { + let mut transaction = RlpStream::new(); + tx.rlp_append(&mut transaction); + let appended = packet.append_raw_checked(&transaction.drain(), 1, MAX_TRANSACTION_PACKET_SIZE); + if !appended { + // Maximal packet size reached just proceed with sending + debug!("Transaction packet size limit reached. Sending incomplete set of {}/{} transactions.", pushed, to_send.len()); + to_send = to_send.into_iter().take(pushed).collect(); + break; + } + pushed += 1; + } + } + packet.complete_unbounded_list(); + (packet, to_send) + }; + + // Update stats + let id = io.peer_session_info(peer_id).and_then(|info| info.id); + for hash in &to_send { + // update stats + stats.propagated(hash, id, block_number); + } + + peer_info.last_sent_transactions = all_transactions_hashes + .intersection(&peer_info.last_sent_transactions) + .chain(&to_send) + .cloned() + .collect(); + Some((peer_id, to_send.len(), packet.out())) + }) + .collect::>() + }; + + // Send RLPs + let mut peers = HashSet::new(); + if lucky_peers.len() > 0 { + let mut max_sent = 0; + let lucky_peers_len = lucky_peers.len(); + for (peer_id, sent, rlp) in lucky_peers { + peers.insert(peer_id); + SyncPropagator::send_packet(io, peer_id, TRANSACTIONS_PACKET, rlp); + trace!(target: "sync", "{:02} <- Transactions ({} entries)", peer_id, sent); + max_sent = cmp::max(max_sent, sent); + } + debug!(target: "sync", "Sent up to {} transactions to {} peers.", max_sent, lucky_peers_len); + } + + peers + } + + pub fn propagate_latest_blocks(sync: &mut ChainSync, io: &mut SyncIo, sealed: &[H256]) { + let chain_info = io.chain().chain_info(); + if (((chain_info.best_block_number as i64) - (sync.last_sent_block_number as i64)).abs() as BlockNumber) < MAX_PEER_LAG_PROPAGATION { + let mut peers = sync.get_lagging_peers(&chain_info); + if sealed.is_empty() { + let hashes = SyncPropagator::propagate_new_hashes(sync, &chain_info, io, &peers); + peers = ChainSync::select_random_peers(&peers); + let blocks = SyncPropagator::propagate_blocks(sync, &chain_info, io, sealed, &peers); + if blocks != 0 || hashes != 0 { + trace!(target: "sync", "Sent latest {} blocks and {} hashes to peers.", blocks, hashes); + } + } else { + SyncPropagator::propagate_blocks(sync, &chain_info, io, sealed, &peers); + SyncPropagator::propagate_new_hashes(sync, &chain_info, io, &peers); + trace!(target: "sync", "Sent sealed block to all peers"); + }; + } + sync.last_sent_block_number = chain_info.best_block_number; + } + + /// Distribute valid proposed blocks to subset of current peers. + pub fn propagate_proposed_blocks(sync: &mut ChainSync, io: &mut SyncIo, proposed: &[Bytes]) { + let peers = sync.get_consensus_peers(); + trace!(target: "sync", "Sending proposed blocks to {:?}", peers); + for block in proposed { + let rlp = ChainSync::create_block_rlp( + block, + io.chain().chain_info().total_difficulty + ); + for peer_id in &peers { + SyncPropagator::send_packet(io, *peer_id, NEW_BLOCK_PACKET, rlp.clone()); + } + } + } + + /// Broadcast consensus message to peers. + pub fn propagate_consensus_packet(sync: &mut ChainSync, io: &mut SyncIo, packet: Bytes) { + let lucky_peers = ChainSync::select_random_peers(&sync.get_consensus_peers()); + trace!(target: "sync", "Sending consensus packet to {:?}", lucky_peers); + for peer_id in lucky_peers { + SyncPropagator::send_packet(io, peer_id, CONSENSUS_DATA_PACKET, packet.clone()); + } + } + + /// Broadcast private transaction message to peers. + pub fn propagate_private_transaction(sync: &mut ChainSync, io: &mut SyncIo, packet: Bytes) { + let lucky_peers = ChainSync::select_random_peers(&sync.get_private_transaction_peers()); + trace!(target: "sync", "Sending private transaction packet to {:?}", lucky_peers); + for peer_id in lucky_peers { + SyncPropagator::send_packet(io, peer_id, PRIVATE_TRANSACTION_PACKET, packet.clone()); + } + } + + /// Broadcast signed private transaction message to peers. + pub fn propagate_signed_private_transaction(sync: &mut ChainSync, io: &mut SyncIo, packet: Bytes) { + let lucky_peers = ChainSync::select_random_peers(&sync.get_private_transaction_peers()); + trace!(target: "sync", "Sending signed private transaction packet to {:?}", lucky_peers); + for peer_id in lucky_peers { + SyncPropagator::send_packet(io, peer_id, SIGNED_PRIVATE_TRANSACTION_PACKET, packet.clone()); + } + } + + fn select_peers_for_transactions(sync: &ChainSync, filter: F) -> Vec + where F: Fn(&PeerId) -> bool { + // sqrt(x)/x scaled to max u32 + let fraction = ((sync.peers.len() as f64).powf(-0.5) * (u32::max_value() as f64).round()) as u32; + let small = sync.peers.len() < MIN_PEERS_PROPAGATION; + + let mut random = random::new(); + sync.peers.keys() + .cloned() + .filter(filter) + .filter(|_| small || random.next_u32() < fraction) + .take(MAX_PEERS_PROPAGATION) + .collect() + } + + /// Generic packet sender + fn send_packet(sync: &mut SyncIo, peer_id: PeerId, packet_id: PacketId, packet: Bytes) { + if let Err(e) = sync.send(peer_id, packet_id, packet) { + debug!(target:"sync", "Error sending packet: {:?}", e); + sync.disconnect_peer(peer_id); + } + } +} + +#[cfg(test)] +mod tests { + use ethcore::client::{BlockInfo, ChainInfo, EachBlockWith, TestBlockChainClient}; + use parking_lot::RwLock; + use private_tx::NoopPrivateTxHandler; + use rlp::{Rlp}; + use std::collections::{VecDeque}; + use tests::helpers::{TestIo}; + use tests::snapshot::TestSnapshotService; + + use super::{*, super::{*, tests::*}}; + + #[test] + fn sends_new_hashes_to_lagging_peer() { + let mut client = TestBlockChainClient::new(); + client.add_blocks(100, EachBlockWith::Uncle); + let queue = RwLock::new(VecDeque::new()); + let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); + let chain_info = client.chain_info(); + let ss = TestSnapshotService::new(); + let mut io = TestIo::new(&mut client, &ss, &queue, None); + + let peers = sync.get_lagging_peers(&chain_info); + let peer_count = SyncPropagator::propagate_new_hashes(&mut sync, &chain_info, &mut io, &peers); + + // 1 message should be send + assert_eq!(1, io.packets.len()); + // 1 peer should be updated + assert_eq!(1, peer_count); + // NEW_BLOCK_HASHES_PACKET + assert_eq!(0x01, io.packets[0].packet_id); + } + + #[test] + fn sends_latest_block_to_lagging_peer() { + let mut client = TestBlockChainClient::new(); + client.add_blocks(100, EachBlockWith::Uncle); + let queue = RwLock::new(VecDeque::new()); + let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); + let chain_info = client.chain_info(); + let ss = TestSnapshotService::new(); + let mut io = TestIo::new(&mut client, &ss, &queue, None); + let peers = sync.get_lagging_peers(&chain_info); + let peer_count = SyncPropagator::propagate_blocks(&mut sync, &chain_info, &mut io, &[], &peers); + + // 1 message should be send + assert_eq!(1, io.packets.len()); + // 1 peer should be updated + assert_eq!(1, peer_count); + // NEW_BLOCK_PACKET + assert_eq!(0x07, io.packets[0].packet_id); + } + + #[test] + fn sends_sealed_block() { + let mut client = TestBlockChainClient::new(); + client.add_blocks(100, EachBlockWith::Uncle); + let queue = RwLock::new(VecDeque::new()); + let hash = client.block_hash(BlockId::Number(99)).unwrap(); + let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); + let chain_info = client.chain_info(); + let ss = TestSnapshotService::new(); + let mut io = TestIo::new(&mut client, &ss, &queue, None); + let peers = sync.get_lagging_peers(&chain_info); + let peer_count = SyncPropagator::propagate_blocks(&mut sync ,&chain_info, &mut io, &[hash.clone()], &peers); + + // 1 message should be send + assert_eq!(1, io.packets.len()); + // 1 peer should be updated + assert_eq!(1, peer_count); + // NEW_BLOCK_PACKET + assert_eq!(0x07, io.packets[0].packet_id); + } + + #[test] + fn sends_proposed_block() { + let mut client = TestBlockChainClient::new(); + client.add_blocks(2, EachBlockWith::Uncle); + let queue = RwLock::new(VecDeque::new()); + let block = client.block(BlockId::Latest).unwrap().into_inner(); + let mut sync = ChainSync::new(SyncConfig::default(), &client, Arc::new(NoopPrivateTxHandler)); + sync.peers.insert(0, + PeerInfo { + // Messaging protocol + protocol_version: 2, + genesis: H256::zero(), + network_id: 0, + latest_hash: client.block_hash_delta_minus(1), + difficulty: None, + asking: PeerAsking::Nothing, + asking_blocks: Vec::new(), + asking_hash: None, + ask_time: Instant::now(), + last_sent_transactions: HashSet::new(), + expired: false, + confirmation: ForkConfirmation::Confirmed, + snapshot_number: None, + snapshot_hash: None, + asking_snapshot_data: None, + block_set: None, + }); + let ss = TestSnapshotService::new(); + let mut io = TestIo::new(&mut client, &ss, &queue, None); + SyncPropagator::propagate_proposed_blocks(&mut sync, &mut io, &[block]); + + // 1 message should be sent + assert_eq!(1, io.packets.len()); + // NEW_BLOCK_PACKET + assert_eq!(0x07, io.packets[0].packet_id); + } + + #[test] + fn propagates_transactions() { + let mut client = TestBlockChainClient::new(); + client.add_blocks(100, EachBlockWith::Uncle); + client.insert_transaction_to_queue(); + let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(1), &client); + let queue = RwLock::new(VecDeque::new()); + let ss = TestSnapshotService::new(); + let mut io = TestIo::new(&mut client, &ss, &queue, None); + let peer_count = SyncPropagator::propagate_new_transactions(&mut sync, &mut io); + // Try to propagate same transactions for the second time + let peer_count2 = SyncPropagator::propagate_new_transactions(&mut sync, &mut io); + // Even after new block transactions should not be propagated twice + sync.chain_new_blocks(&mut io, &[], &[], &[], &[], &[], &[]); + // Try to propagate same transactions for the third time + let peer_count3 = SyncPropagator::propagate_new_transactions(&mut sync, &mut io); + + // 1 message should be send + assert_eq!(1, io.packets.len()); + // 1 peer should be updated but only once + assert_eq!(1, peer_count); + assert_eq!(0, peer_count2); + assert_eq!(0, peer_count3); + // TRANSACTIONS_PACKET + assert_eq!(0x02, io.packets[0].packet_id); + } + + #[test] + fn does_not_propagate_new_transactions_after_new_block() { + let mut client = TestBlockChainClient::new(); + client.add_blocks(100, EachBlockWith::Uncle); + client.insert_transaction_to_queue(); + let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(1), &client); + let queue = RwLock::new(VecDeque::new()); + let ss = TestSnapshotService::new(); + let mut io = TestIo::new(&mut client, &ss, &queue, None); + let peer_count = SyncPropagator::propagate_new_transactions(&mut sync, &mut io); + io.chain.insert_transaction_to_queue(); + // New block import should not trigger propagation. + // (we only propagate on timeout) + sync.chain_new_blocks(&mut io, &[], &[], &[], &[], &[], &[]); + + // 2 message should be send + assert_eq!(1, io.packets.len()); + // 1 peer should receive the message + assert_eq!(1, peer_count); + // TRANSACTIONS_PACKET + assert_eq!(0x02, io.packets[0].packet_id); + } + + #[test] + fn does_not_fail_for_no_peers() { + let mut client = TestBlockChainClient::new(); + client.add_blocks(100, EachBlockWith::Uncle); + client.insert_transaction_to_queue(); + // Sync with no peers + let mut sync = ChainSync::new(SyncConfig::default(), &client, Arc::new(NoopPrivateTxHandler)); + let queue = RwLock::new(VecDeque::new()); + let ss = TestSnapshotService::new(); + let mut io = TestIo::new(&mut client, &ss, &queue, None); + let peer_count = SyncPropagator::propagate_new_transactions(&mut sync, &mut io); + sync.chain_new_blocks(&mut io, &[], &[], &[], &[], &[], &[]); + // Try to propagate same transactions for the second time + let peer_count2 = SyncPropagator::propagate_new_transactions(&mut sync, &mut io); + + assert_eq!(0, io.packets.len()); + assert_eq!(0, peer_count); + assert_eq!(0, peer_count2); + } + + #[test] + fn propagates_transactions_without_alternating() { + let mut client = TestBlockChainClient::new(); + client.add_blocks(100, EachBlockWith::Uncle); + client.insert_transaction_to_queue(); + let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(1), &client); + let queue = RwLock::new(VecDeque::new()); + let ss = TestSnapshotService::new(); + // should sent some + { + let mut io = TestIo::new(&mut client, &ss, &queue, None); + let peer_count = SyncPropagator::propagate_new_transactions(&mut sync, &mut io); + assert_eq!(1, io.packets.len()); + assert_eq!(1, peer_count); + } + // Insert some more + client.insert_transaction_to_queue(); + let (peer_count2, peer_count3) = { + let mut io = TestIo::new(&mut client, &ss, &queue, None); + // Propagate new transactions + let peer_count2 = SyncPropagator::propagate_new_transactions(&mut sync, &mut io); + // And now the peer should have all transactions + let peer_count3 = SyncPropagator::propagate_new_transactions(&mut sync, &mut io); + (peer_count2, peer_count3) + }; + + // 2 message should be send (in total) + assert_eq!(2, queue.read().len()); + // 1 peer should be updated but only once after inserting new transaction + assert_eq!(1, peer_count2); + assert_eq!(0, peer_count3); + // TRANSACTIONS_PACKET + assert_eq!(0x02, queue.read()[0].packet_id); + assert_eq!(0x02, queue.read()[1].packet_id); + } + + #[test] + fn should_maintain_transations_propagation_stats() { + let mut client = TestBlockChainClient::new(); + client.add_blocks(100, EachBlockWith::Uncle); + client.insert_transaction_to_queue(); + let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(1), &client); + let queue = RwLock::new(VecDeque::new()); + let ss = TestSnapshotService::new(); + let mut io = TestIo::new(&mut client, &ss, &queue, None); + SyncPropagator::propagate_new_transactions(&mut sync, &mut io); + + let stats = sync.transactions_stats(); + assert_eq!(stats.len(), 1, "Should maintain stats for single transaction.") + } + + #[test] + fn should_propagate_service_transaction_to_selected_peers_only() { + let mut client = TestBlockChainClient::new(); + client.insert_transaction_with_gas_price_to_queue(U256::zero()); + let block_hash = client.block_hash_delta_minus(1); + let mut sync = ChainSync::new(SyncConfig::default(), &client, Arc::new(NoopPrivateTxHandler)); + let queue = RwLock::new(VecDeque::new()); + let ss = TestSnapshotService::new(); + let mut io = TestIo::new(&mut client, &ss, &queue, None); + + // when peer#1 is Geth + insert_dummy_peer(&mut sync, 1, block_hash); + io.peers_info.insert(1, "Geth".to_owned()); + // and peer#2 is Parity, accepting service transactions + insert_dummy_peer(&mut sync, 2, block_hash); + io.peers_info.insert(2, "Parity/v1.6".to_owned()); + // and peer#3 is Parity, discarding service transactions + insert_dummy_peer(&mut sync, 3, block_hash); + io.peers_info.insert(3, "Parity/v1.5".to_owned()); + // and peer#4 is Parity, accepting service transactions + insert_dummy_peer(&mut sync, 4, block_hash); + io.peers_info.insert(4, "Parity/v1.7.3-ABCDEFGH".to_owned()); + + // and new service transaction is propagated to peers + SyncPropagator::propagate_new_transactions(&mut sync, &mut io); + + // peer#2 && peer#4 are receiving service transaction + assert!(io.packets.iter().any(|p| p.packet_id == 0x02 && p.recipient == 2)); // TRANSACTIONS_PACKET + assert!(io.packets.iter().any(|p| p.packet_id == 0x02 && p.recipient == 4)); // TRANSACTIONS_PACKET + assert_eq!(io.packets.len(), 2); + } + + #[test] + fn should_propagate_service_transaction_is_sent_as_separate_message() { + let mut client = TestBlockChainClient::new(); + let tx1_hash = client.insert_transaction_to_queue(); + let tx2_hash = client.insert_transaction_with_gas_price_to_queue(U256::zero()); + let block_hash = client.block_hash_delta_minus(1); + let mut sync = ChainSync::new(SyncConfig::default(), &client, Arc::new(NoopPrivateTxHandler)); + let queue = RwLock::new(VecDeque::new()); + let ss = TestSnapshotService::new(); + let mut io = TestIo::new(&mut client, &ss, &queue, None); + + // when peer#1 is Parity, accepting service transactions + insert_dummy_peer(&mut sync, 1, block_hash); + io.peers_info.insert(1, "Parity/v1.6".to_owned()); + + // and service + non-service transactions are propagated to peers + SyncPropagator::propagate_new_transactions(&mut sync, &mut io); + + // two separate packets for peer are queued: + // 1) with non-service-transaction + // 2) with service transaction + let sent_transactions: Vec = io.packets.iter() + .filter_map(|p| { + if p.packet_id != 0x02 || p.recipient != 1 { // TRANSACTIONS_PACKET + return None; + } + + let rlp = Rlp::new(&*p.data); + let item_count = rlp.item_count().unwrap_or(0); + if item_count != 1 { + return None; + } + + rlp.at(0).ok().and_then(|r| r.as_val().ok()) + }) + .collect(); + assert_eq!(sent_transactions.len(), 2); + assert!(sent_transactions.iter().any(|tx| tx.hash() == tx1_hash)); + assert!(sent_transactions.iter().any(|tx| tx.hash() == tx2_hash)); + } +} diff --git a/ethcore/sync/src/chain/requester.rs b/ethcore/sync/src/chain/requester.rs new file mode 100644 index 0000000000000000000000000000000000000000..a85874d29276456d1e156815991d082de5458498 --- /dev/null +++ b/ethcore/sync/src/chain/requester.rs @@ -0,0 +1,155 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use api::WARP_SYNC_PROTOCOL_ID; +use block_sync::BlockRequest; +use bytes::Bytes; +use ethcore::header::BlockNumber; +use ethereum_types::H256; +use network::{PeerId, PacketId}; +use rlp::RlpStream; +use std::time::Instant; +use sync_io::SyncIo; + +use super::{ + BlockSet, + ChainSync, + PeerAsking, + ETH_PROTOCOL_VERSION_63, + GET_BLOCK_BODIES_PACKET, + GET_BLOCK_HEADERS_PACKET, + GET_RECEIPTS_PACKET, + GET_SNAPSHOT_DATA_PACKET, + GET_SNAPSHOT_MANIFEST_PACKET, +}; + +/// The Chain Sync Requester: requesting data to other peers +pub struct SyncRequester; + +impl SyncRequester { + /// Perform block download request` + pub fn request_blocks(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, request: BlockRequest, block_set: BlockSet) { + match request { + BlockRequest::Headers { start, count, skip } => { + SyncRequester::request_headers_by_hash(sync, io, peer_id, &start, count, skip, false, block_set); + }, + BlockRequest::Bodies { hashes } => { + SyncRequester::request_bodies(sync, io, peer_id, hashes, block_set); + }, + BlockRequest::Receipts { hashes } => { + SyncRequester::request_receipts(sync, io, peer_id, hashes, block_set); + }, + } + } + + /// Request block bodies from a peer + fn request_bodies(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, hashes: Vec, set: BlockSet) { + let mut rlp = RlpStream::new_list(hashes.len()); + trace!(target: "sync", "{} <- GetBlockBodies: {} entries starting from {:?}, set = {:?}", peer_id, hashes.len(), hashes.first(), set); + for h in &hashes { + rlp.append(&h.clone()); + } + SyncRequester::send_request(sync, io, peer_id, PeerAsking::BlockBodies, GET_BLOCK_BODIES_PACKET, rlp.out()); + let peer = sync.peers.get_mut(&peer_id).expect("peer_id may originate either from on_packet, where it is already validated or from enumerating self.peers. qed"); + peer.asking_blocks = hashes; + peer.block_set = Some(set); + } + + /// Request headers from a peer by block number + pub fn request_fork_header(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, n: BlockNumber) { + trace!(target: "sync", "{} <- GetForkHeader: at {}", peer_id, n); + let mut rlp = RlpStream::new_list(4); + rlp.append(&n); + rlp.append(&1u32); + rlp.append(&0u32); + rlp.append(&0u32); + SyncRequester::send_request(sync, io, peer_id, PeerAsking::ForkHeader, GET_BLOCK_HEADERS_PACKET, rlp.out()); + } + + /// Find some headers or blocks to download for a peer. + pub fn request_snapshot_data(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId) { + // find chunk data to download + if let Some(hash) = sync.snapshot.needed_chunk() { + if let Some(ref mut peer) = sync.peers.get_mut(&peer_id) { + peer.asking_snapshot_data = Some(hash.clone()); + } + SyncRequester::request_snapshot_chunk(sync, io, peer_id, &hash); + } + } + + /// Request snapshot manifest from a peer. + pub fn request_snapshot_manifest(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId) { + trace!(target: "sync", "{} <- GetSnapshotManifest", peer_id); + let rlp = RlpStream::new_list(0); + SyncRequester::send_request(sync, io, peer_id, PeerAsking::SnapshotManifest, GET_SNAPSHOT_MANIFEST_PACKET, rlp.out()); + } + + /// Request headers from a peer by block hash + fn request_headers_by_hash(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, h: &H256, count: u64, skip: u64, reverse: bool, set: BlockSet) { + trace!(target: "sync", "{} <- GetBlockHeaders: {} entries starting from {}, set = {:?}", peer_id, count, h, set); + let mut rlp = RlpStream::new_list(4); + rlp.append(h); + rlp.append(&count); + rlp.append(&skip); + rlp.append(&if reverse {1u32} else {0u32}); + SyncRequester::send_request(sync, io, peer_id, PeerAsking::BlockHeaders, GET_BLOCK_HEADERS_PACKET, rlp.out()); + let peer = sync.peers.get_mut(&peer_id).expect("peer_id may originate either from on_packet, where it is already validated or from enumerating self.peers. qed"); + peer.asking_hash = Some(h.clone()); + peer.block_set = Some(set); + } + + /// Request block receipts from a peer + fn request_receipts(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, hashes: Vec, set: BlockSet) { + let mut rlp = RlpStream::new_list(hashes.len()); + trace!(target: "sync", "{} <- GetBlockReceipts: {} entries starting from {:?}, set = {:?}", peer_id, hashes.len(), hashes.first(), set); + for h in &hashes { + rlp.append(&h.clone()); + } + SyncRequester::send_request(sync, io, peer_id, PeerAsking::BlockReceipts, GET_RECEIPTS_PACKET, rlp.out()); + let peer = sync.peers.get_mut(&peer_id).expect("peer_id may originate either from on_packet, where it is already validated or from enumerating self.peers. qed"); + peer.asking_blocks = hashes; + peer.block_set = Some(set); + } + + /// Request snapshot chunk from a peer. + fn request_snapshot_chunk(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, chunk: &H256) { + trace!(target: "sync", "{} <- GetSnapshotData {:?}", peer_id, chunk); + let mut rlp = RlpStream::new_list(1); + rlp.append(chunk); + SyncRequester::send_request(sync, io, peer_id, PeerAsking::SnapshotData, GET_SNAPSHOT_DATA_PACKET, rlp.out()); + } + + /// Generic request sender + fn send_request(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, asking: PeerAsking, packet_id: PacketId, packet: Bytes) { + if let Some(ref mut peer) = sync.peers.get_mut(&peer_id) { + if peer.asking != PeerAsking::Nothing { + warn!(target:"sync", "Asking {:?} while requesting {:?}", peer.asking, asking); + } + peer.asking = asking; + peer.ask_time = Instant::now(); + // TODO [ToDr] This seems quite fragile. Be careful when protocol is updated. + let result = if packet_id >= ETH_PROTOCOL_VERSION_63.1 { + io.send_protocol(WARP_SYNC_PROTOCOL_ID, peer_id, packet_id, packet) + } else { + io.send(peer_id, packet_id, packet) + }; + if let Err(e) = result { + debug!(target:"sync", "Error sending request: {:?}", e); + io.disconnect_peer(peer_id); + } + } + } +} diff --git a/ethcore/sync/src/chain/supplier.rs b/ethcore/sync/src/chain/supplier.rs new file mode 100644 index 0000000000000000000000000000000000000000..9b6efbacbb7b83f84bb146b0ab571fe8784becc4 --- /dev/null +++ b/ethcore/sync/src/chain/supplier.rs @@ -0,0 +1,450 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use bytes::Bytes; +use ethcore::client::BlockId; +use ethcore::header::BlockNumber; +use ethereum_types::H256; +use network::{self, PeerId}; +use parking_lot::RwLock; +use rlp::{Rlp, RlpStream}; +use std::cmp; +use sync_io::SyncIo; + +use super::{ + ChainSync, + RlpResponseResult, + PacketDecodeError, + BLOCK_BODIES_PACKET, + BLOCK_HEADERS_PACKET, + CONSENSUS_DATA_PACKET, + GET_BLOCK_BODIES_PACKET, + GET_BLOCK_HEADERS_PACKET, + GET_NODE_DATA_PACKET, + GET_RECEIPTS_PACKET, + GET_SNAPSHOT_DATA_PACKET, + GET_SNAPSHOT_MANIFEST_PACKET, + MAX_BODIES_TO_SEND, + MAX_HEADERS_TO_SEND, + MAX_NODE_DATA_TO_SEND, + MAX_RECEIPTS_HEADERS_TO_SEND, + MAX_RECEIPTS_TO_SEND, + NODE_DATA_PACKET, + RECEIPTS_PACKET, + SNAPSHOT_DATA_PACKET, + SNAPSHOT_MANIFEST_PACKET, +}; + +/// The Chain Sync Supplier: answers requests from peers with available data +pub struct SyncSupplier; + +impl SyncSupplier { + /// Dispatch incoming requests and responses + pub fn dispatch_packet(sync: &RwLock, io: &mut SyncIo, peer: PeerId, packet_id: u8, data: &[u8]) { + let rlp = Rlp::new(data); + let result = match packet_id { + GET_BLOCK_BODIES_PACKET => SyncSupplier::return_rlp(io, &rlp, peer, + SyncSupplier::return_block_bodies, + |e| format!("Error sending block bodies: {:?}", e)), + + GET_BLOCK_HEADERS_PACKET => SyncSupplier::return_rlp(io, &rlp, peer, + SyncSupplier::return_block_headers, + |e| format!("Error sending block headers: {:?}", e)), + + GET_RECEIPTS_PACKET => SyncSupplier::return_rlp(io, &rlp, peer, + SyncSupplier::return_receipts, + |e| format!("Error sending receipts: {:?}", e)), + + GET_NODE_DATA_PACKET => SyncSupplier::return_rlp(io, &rlp, peer, + SyncSupplier::return_node_data, + |e| format!("Error sending nodes: {:?}", e)), + + GET_SNAPSHOT_MANIFEST_PACKET => SyncSupplier::return_rlp(io, &rlp, peer, + SyncSupplier::return_snapshot_manifest, + |e| format!("Error sending snapshot manifest: {:?}", e)), + + GET_SNAPSHOT_DATA_PACKET => SyncSupplier::return_rlp(io, &rlp, peer, + SyncSupplier::return_snapshot_data, + |e| format!("Error sending snapshot data: {:?}", e)), + CONSENSUS_DATA_PACKET => ChainSync::on_consensus_packet(io, peer, &rlp), + _ => { + sync.write().on_packet(io, peer, packet_id, data); + Ok(()) + } + }; + result.unwrap_or_else(|e| { + debug!(target:"sync", "{} -> Malformed packet {} : {}", peer, packet_id, e); + }) + } + + /// Respond to GetBlockHeaders request + fn return_block_headers(io: &SyncIo, r: &Rlp, peer_id: PeerId) -> RlpResponseResult { + // Packet layout: + // [ block: { P , B_32 }, maxHeaders: P, skip: P, reverse: P in { 0 , 1 } ] + let max_headers: usize = r.val_at(1)?; + let skip: usize = r.val_at(2)?; + let reverse: bool = r.val_at(3)?; + let last = io.chain().chain_info().best_block_number; + let number = if r.at(0)?.size() == 32 { + // id is a hash + let hash: H256 = r.val_at(0)?; + trace!(target: "sync", "{} -> GetBlockHeaders (hash: {}, max: {}, skip: {}, reverse:{})", peer_id, hash, max_headers, skip, reverse); + match io.chain().block_header(BlockId::Hash(hash)) { + Some(hdr) => { + let number = hdr.number().into(); + debug_assert_eq!(hdr.hash(), hash); + + if max_headers == 1 || io.chain().block_hash(BlockId::Number(number)) != Some(hash) { + // Non canonical header or single header requested + // TODO: handle single-step reverse hashchains of non-canon hashes + trace!(target:"sync", "Returning single header: {:?}", hash); + let mut rlp = RlpStream::new_list(1); + rlp.append_raw(&hdr.into_inner(), 1); + return Ok(Some((BLOCK_HEADERS_PACKET, rlp))); + } + number + } + None => return Ok(Some((BLOCK_HEADERS_PACKET, RlpStream::new_list(0)))) //no such header, return nothing + } + } else { + let number = r.val_at::(0)?; + trace!(target: "sync", "{} -> GetBlockHeaders (number: {}, max: {}, skip: {}, reverse:{})", peer_id, number, max_headers, skip, reverse); + number + }; + + let mut number = if reverse { + cmp::min(last, number) + } else { + cmp::max(0, number) + }; + let max_count = cmp::min(MAX_HEADERS_TO_SEND, max_headers); + let mut count = 0; + let mut data = Bytes::new(); + let inc = skip.saturating_add(1) as BlockNumber; + let overlay = io.chain_overlay().read(); + + // We are checking the `overlay` as well since it's where the ForkBlock + // header is cached : so peers can confirm we are on the right fork, + // even if we are not synced until the fork block + while (number <= last || overlay.contains_key(&number)) && count < max_count { + if let Some(hdr) = overlay.get(&number) { + trace!(target: "sync", "{}: Returning cached fork header", peer_id); + data.extend_from_slice(hdr); + count += 1; + } else if let Some(hdr) = io.chain().block_header(BlockId::Number(number)) { + data.append(&mut hdr.into_inner()); + count += 1; + } else { + // No required block. + break; + } + if reverse { + if number <= inc || number == 0 { + break; + } + number = number.saturating_sub(inc); + } else { + number = number.saturating_add(inc); + } + } + let mut rlp = RlpStream::new_list(count as usize); + rlp.append_raw(&data, count as usize); + trace!(target: "sync", "{} -> GetBlockHeaders: returned {} entries", peer_id, count); + Ok(Some((BLOCK_HEADERS_PACKET, rlp))) + } + + /// Respond to GetBlockBodies request + fn return_block_bodies(io: &SyncIo, r: &Rlp, peer_id: PeerId) -> RlpResponseResult { + let mut count = r.item_count().unwrap_or(0); + if count == 0 { + debug!(target: "sync", "Empty GetBlockBodies request, ignoring."); + return Ok(None); + } + count = cmp::min(count, MAX_BODIES_TO_SEND); + let mut added = 0usize; + let mut data = Bytes::new(); + for i in 0..count { + if let Some(body) = io.chain().block_body(BlockId::Hash(r.val_at::(i)?)) { + data.append(&mut body.into_inner()); + added += 1; + } + } + let mut rlp = RlpStream::new_list(added); + rlp.append_raw(&data, added); + trace!(target: "sync", "{} -> GetBlockBodies: returned {} entries", peer_id, added); + Ok(Some((BLOCK_BODIES_PACKET, rlp))) + } + + /// Respond to GetNodeData request + fn return_node_data(io: &SyncIo, r: &Rlp, peer_id: PeerId) -> RlpResponseResult { + let mut count = r.item_count().unwrap_or(0); + trace!(target: "sync", "{} -> GetNodeData: {} entries", peer_id, count); + if count == 0 { + debug!(target: "sync", "Empty GetNodeData request, ignoring."); + return Ok(None); + } + count = cmp::min(count, MAX_NODE_DATA_TO_SEND); + let mut added = 0usize; + let mut data = Vec::new(); + for i in 0..count { + if let Some(node) = io.chain().state_data(&r.val_at::(i)?) { + data.push(node); + added += 1; + } + } + trace!(target: "sync", "{} -> GetNodeData: return {} entries", peer_id, added); + let mut rlp = RlpStream::new_list(added); + for d in data { + rlp.append(&d); + } + Ok(Some((NODE_DATA_PACKET, rlp))) + } + + fn return_receipts(io: &SyncIo, rlp: &Rlp, peer_id: PeerId) -> RlpResponseResult { + let mut count = rlp.item_count().unwrap_or(0); + trace!(target: "sync", "{} -> GetReceipts: {} entries", peer_id, count); + if count == 0 { + debug!(target: "sync", "Empty GetReceipts request, ignoring."); + return Ok(None); + } + count = cmp::min(count, MAX_RECEIPTS_HEADERS_TO_SEND); + let mut added_headers = 0usize; + let mut added_receipts = 0usize; + let mut data = Bytes::new(); + for i in 0..count { + if let Some(mut receipts_bytes) = io.chain().block_receipts(&rlp.val_at::(i)?) { + data.append(&mut receipts_bytes); + added_receipts += receipts_bytes.len(); + added_headers += 1; + if added_receipts > MAX_RECEIPTS_TO_SEND { break; } + } + } + let mut rlp_result = RlpStream::new_list(added_headers); + rlp_result.append_raw(&data, added_headers); + Ok(Some((RECEIPTS_PACKET, rlp_result))) + } + + /// Respond to GetSnapshotManifest request + fn return_snapshot_manifest(io: &SyncIo, r: &Rlp, peer_id: PeerId) -> RlpResponseResult { + let count = r.item_count().unwrap_or(0); + trace!(target: "warp", "{} -> GetSnapshotManifest", peer_id); + if count != 0 { + debug!(target: "warp", "Invalid GetSnapshotManifest request, ignoring."); + return Ok(None); + } + let rlp = match io.snapshot_service().manifest() { + Some(manifest) => { + trace!(target: "warp", "{} <- SnapshotManifest", peer_id); + let mut rlp = RlpStream::new_list(1); + rlp.append_raw(&manifest.into_rlp(), 1); + rlp + }, + None => { + trace!(target: "warp", "{}: No snapshot manifest to return", peer_id); + RlpStream::new_list(0) + } + }; + Ok(Some((SNAPSHOT_MANIFEST_PACKET, rlp))) + } + + /// Respond to GetSnapshotData request + fn return_snapshot_data(io: &SyncIo, r: &Rlp, peer_id: PeerId) -> RlpResponseResult { + let hash: H256 = r.val_at(0)?; + trace!(target: "warp", "{} -> GetSnapshotData {:?}", peer_id, hash); + let rlp = match io.snapshot_service().chunk(hash) { + Some(data) => { + let mut rlp = RlpStream::new_list(1); + trace!(target: "warp", "{} <- SnapshotData", peer_id); + rlp.append(&data); + rlp + }, + None => { + trace!(target: "warp", "{}: No snapshot data to return", peer_id); + RlpStream::new_list(0) + } + }; + Ok(Some((SNAPSHOT_DATA_PACKET, rlp))) + } + + fn return_rlp(io: &mut SyncIo, rlp: &Rlp, peer: PeerId, rlp_func: FRlp, error_func: FError) -> Result<(), PacketDecodeError> + where FRlp : Fn(&SyncIo, &Rlp, PeerId) -> RlpResponseResult, + FError : FnOnce(network::Error) -> String + { + let response = rlp_func(io, rlp, peer); + match response { + Err(e) => Err(e), + Ok(Some((packet_id, rlp_stream))) => { + io.respond(packet_id, rlp_stream.out()).unwrap_or_else( + |e| debug!(target: "sync", "{:?}", error_func(e))); + Ok(()) + } + _ => Ok(()) + } + } +} + +#[cfg(test)] +mod test { + use std::collections::{VecDeque}; + use tests::helpers::{TestIo}; + use tests::snapshot::TestSnapshotService; + use ethereum_types::{H256}; + use parking_lot::RwLock; + use bytes::Bytes; + use rlp::{Rlp, RlpStream}; + use super::{*, super::tests::*}; + use ethcore::client::{BlockChainClient, EachBlockWith, TestBlockChainClient}; + + #[test] + fn return_block_headers() { + use ethcore::views::HeaderView; + fn make_hash_req(h: &H256, count: usize, skip: usize, reverse: bool) -> Bytes { + let mut rlp = RlpStream::new_list(4); + rlp.append(h); + rlp.append(&count); + rlp.append(&skip); + rlp.append(&if reverse {1u32} else {0u32}); + rlp.out() + } + + fn make_num_req(n: usize, count: usize, skip: usize, reverse: bool) -> Bytes { + let mut rlp = RlpStream::new_list(4); + rlp.append(&n); + rlp.append(&count); + rlp.append(&skip); + rlp.append(&if reverse {1u32} else {0u32}); + rlp.out() + } + fn to_header_vec(rlp: ::chain::RlpResponseResult) -> Vec { + Rlp::new(&rlp.unwrap().unwrap().1.out()).iter().map(|r| r.as_raw().to_vec()).collect() + } + + let mut client = TestBlockChainClient::new(); + client.add_blocks(100, EachBlockWith::Nothing); + let blocks: Vec<_> = (0 .. 100) + .map(|i| (&client as &BlockChainClient).block(BlockId::Number(i as BlockNumber)).map(|b| b.into_inner()).unwrap()).collect(); + let headers: Vec<_> = blocks.iter().map(|b| Rlp::new(b).at(0).unwrap().as_raw().to_vec()).collect(); + let hashes: Vec<_> = headers.iter().map(|h| view!(HeaderView, h).hash()).collect(); + + let queue = RwLock::new(VecDeque::new()); + let ss = TestSnapshotService::new(); + let io = TestIo::new(&mut client, &ss, &queue, None); + + let unknown: H256 = H256::new(); + let result = SyncSupplier::return_block_headers(&io, &Rlp::new(&make_hash_req(&unknown, 1, 0, false)), 0); + assert!(to_header_vec(result).is_empty()); + let result = SyncSupplier::return_block_headers(&io, &Rlp::new(&make_hash_req(&unknown, 1, 0, true)), 0); + assert!(to_header_vec(result).is_empty()); + + let result = SyncSupplier::return_block_headers(&io, &Rlp::new(&make_hash_req(&hashes[2], 1, 0, true)), 0); + assert_eq!(to_header_vec(result), vec![headers[2].clone()]); + + let result = SyncSupplier::return_block_headers(&io, &Rlp::new(&make_hash_req(&hashes[2], 1, 0, false)), 0); + assert_eq!(to_header_vec(result), vec![headers[2].clone()]); + + let result = SyncSupplier::return_block_headers(&io, &Rlp::new(&make_hash_req(&hashes[50], 3, 5, false)), 0); + assert_eq!(to_header_vec(result), vec![headers[50].clone(), headers[56].clone(), headers[62].clone()]); + + let result = SyncSupplier::return_block_headers(&io, &Rlp::new(&make_hash_req(&hashes[50], 3, 5, true)), 0); + assert_eq!(to_header_vec(result), vec![headers[50].clone(), headers[44].clone(), headers[38].clone()]); + + let result = SyncSupplier::return_block_headers(&io, &Rlp::new(&make_num_req(2, 1, 0, true)), 0); + assert_eq!(to_header_vec(result), vec![headers[2].clone()]); + + let result = SyncSupplier::return_block_headers(&io, &Rlp::new(&make_num_req(2, 1, 0, false)), 0); + assert_eq!(to_header_vec(result), vec![headers[2].clone()]); + + let result = SyncSupplier::return_block_headers(&io, &Rlp::new(&make_num_req(50, 3, 5, false)), 0); + assert_eq!(to_header_vec(result), vec![headers[50].clone(), headers[56].clone(), headers[62].clone()]); + + let result = SyncSupplier::return_block_headers(&io, &Rlp::new(&make_num_req(50, 3, 5, true)), 0); + assert_eq!(to_header_vec(result), vec![headers[50].clone(), headers[44].clone(), headers[38].clone()]); + } + + #[test] + fn return_nodes() { + let mut client = TestBlockChainClient::new(); + let queue = RwLock::new(VecDeque::new()); + let sync = dummy_sync_with_peer(H256::new(), &client); + let ss = TestSnapshotService::new(); + let mut io = TestIo::new(&mut client, &ss, &queue, None); + + let mut node_list = RlpStream::new_list(3); + node_list.append(&H256::from("0000000000000000000000000000000000000000000000005555555555555555")); + node_list.append(&H256::from("ffffffffffffffffffffffffffffffffffffffffffffaaaaaaaaaaaaaaaaaaaa")); + node_list.append(&H256::from("aff0000000000000000000000000000000000000000000000000000000000000")); + + let node_request = node_list.out(); + // it returns rlp ONLY for hashes started with "f" + let result = SyncSupplier::return_node_data(&io, &Rlp::new(&node_request.clone()), 0); + + assert!(result.is_ok()); + let rlp_result = result.unwrap(); + assert!(rlp_result.is_some()); + + // the length of one rlp-encoded hashe + let rlp = rlp_result.unwrap().1.out(); + let rlp = Rlp::new(&rlp); + assert_eq!(Ok(1), rlp.item_count()); + + io.sender = Some(2usize); + + ChainSync::dispatch_packet(&RwLock::new(sync), &mut io, 0usize, GET_NODE_DATA_PACKET, &node_request); + assert_eq!(1, io.packets.len()); + } + + #[test] + fn return_receipts_empty() { + let mut client = TestBlockChainClient::new(); + let queue = RwLock::new(VecDeque::new()); + let ss = TestSnapshotService::new(); + let io = TestIo::new(&mut client, &ss, &queue, None); + + let result = SyncSupplier::return_receipts(&io, &Rlp::new(&[0xc0]), 0); + + assert!(result.is_ok()); + } + + #[test] + fn return_receipts() { + let mut client = TestBlockChainClient::new(); + let queue = RwLock::new(VecDeque::new()); + let sync = dummy_sync_with_peer(H256::new(), &client); + let ss = TestSnapshotService::new(); + let mut io = TestIo::new(&mut client, &ss, &queue, None); + + let mut receipt_list = RlpStream::new_list(4); + receipt_list.append(&H256::from("0000000000000000000000000000000000000000000000005555555555555555")); + receipt_list.append(&H256::from("ff00000000000000000000000000000000000000000000000000000000000000")); + receipt_list.append(&H256::from("fff0000000000000000000000000000000000000000000000000000000000000")); + receipt_list.append(&H256::from("aff0000000000000000000000000000000000000000000000000000000000000")); + + let receipts_request = receipt_list.out(); + // it returns rlp ONLY for hashes started with "f" + let result = SyncSupplier::return_receipts(&io, &Rlp::new(&receipts_request.clone()), 0); + + assert!(result.is_ok()); + let rlp_result = result.unwrap(); + assert!(rlp_result.is_some()); + + // the length of two rlp-encoded receipts + assert_eq!(603, rlp_result.unwrap().1.out().len()); + + io.sender = Some(2usize); + ChainSync::dispatch_packet(&RwLock::new(sync), &mut io, 0usize, GET_RECEIPTS_PACKET, &receipts_request); + assert_eq!(1, io.packets.len()); + } +} diff --git a/sync/src/lib.rs b/ethcore/sync/src/lib.rs similarity index 80% rename from sync/src/lib.rs rename to ethcore/sync/src/lib.rs index 9aee8f3d4ef1c7703429761b036ca7a3f975e169..35483f4ec3a464e800a3d5a4d5c3b66760e4f20c 100644 --- a/sync/src/lib.rs +++ b/ethcore/sync/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -26,6 +26,7 @@ extern crate ethcore_network_devp2p as devp2p; extern crate ethcore_bytes as bytes; extern crate ethcore_io as io; extern crate ethcore_transaction as transaction; +#[macro_use] extern crate ethcore; extern crate ethereum_types; extern crate env_logger; @@ -44,6 +45,8 @@ extern crate ethcore_light as light; #[cfg(test)] extern crate ethkey; #[cfg(test)] extern crate kvdb_memorydb; +#[cfg(test)] extern crate rustc_hex; +#[cfg(test)] extern crate ethcore_private_tx; #[macro_use] extern crate macros; @@ -51,11 +54,14 @@ extern crate macros; extern crate log; #[macro_use] extern crate heapsize; +#[macro_use] +extern crate trace_time; mod chain; mod blocks; mod block_sync; mod sync_io; +mod private_tx; mod snapshot; mod transactions_stats; @@ -68,5 +74,6 @@ mod api; pub use api::*; pub use chain::{SyncStatus, SyncState}; -pub use devp2p::{validate_node_url, ConnectionFilter, ConnectionDirection}; -pub use network::{NonReservedPeerMode, Error, ErrorKind}; +pub use devp2p::validate_node_url; +pub use network::{NonReservedPeerMode, Error, ErrorKind, ConnectionFilter, ConnectionDirection}; +pub use private_tx::{PrivateTxHandler, NoopPrivateTxHandler, SimplePrivateTxHandler}; diff --git a/sync/src/light_sync/mod.rs b/ethcore/sync/src/light_sync/mod.rs similarity index 93% rename from sync/src/light_sync/mod.rs rename to ethcore/sync/src/light_sync/mod.rs index ef1bd8742f3e233747b1c77dbad6741d9c767d13..32e3a0dbfde2a793498d2ef2338ee5ebf55f3903 100644 --- a/sync/src/light_sync/mod.rs +++ b/ethcore/sync/src/light_sync/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -58,12 +58,12 @@ mod sync_round; #[cfg(test)] mod tests; -// Base number of milliseconds for the header request timeout. -const REQ_TIMEOUT_MILLISECS_BASE: u64 = 7000; -// Additional number of milliseconds for each requested header. +// Base value for the header request timeout. +const REQ_TIMEOUT_BASE: Duration = Duration::from_secs(7); +// Additional value for each requested header. // If we request N headers, then the timeout will be: -// REQ_TIMEOUT_MILLISECS_BASE + N * REQ_TIMEOUT_MILLISECS_PER_HEADER -const REQ_TIMEOUT_MILLISECS_PER_HEADER: u64 = 10; +// REQ_TIMEOUT_BASE + N * REQ_TIMEOUT_PER_HEADER +const REQ_TIMEOUT_PER_HEADER: Duration = Duration::from_millis(10); /// Peer chain info. #[derive(Debug, Clone, PartialEq, Eq)] @@ -427,6 +427,8 @@ impl LightSync { // handles request dispatch, block import, state machine transitions, and timeouts. fn maintain_sync(&self, ctx: &BasicContext) { + use ethcore::error::{BlockImportError, BlockImportErrorKind, ImportErrorKind}; + const DRAIN_AMOUNT: usize = 128; let client = self.client.as_light_client(); @@ -453,11 +455,20 @@ impl LightSync { trace!(target: "sync", "Drained {} headers to import", sink.len()); for header in sink.drain(..) { - if let Err(e) = client.queue_header(header) { - debug!(target: "sync", "Found bad header ({:?}). Reset to search state.", e); + match client.queue_header(header) { + Ok(_) => {} + Err(BlockImportError(BlockImportErrorKind::Import(ImportErrorKind::AlreadyInChain), _)) => { + trace!(target: "sync", "Block already in chain. Continuing."); + }, + Err(BlockImportError(BlockImportErrorKind::Import(ImportErrorKind::AlreadyQueued), _)) => { + trace!(target: "sync", "Block already queued. Continuing."); + }, + Err(e) => { + debug!(target: "sync", "Found bad header ({:?}). Reset to search state.", e); - self.begin_search(&mut state); - break 'a; + self.begin_search(&mut state); + break 'a; + } } } } @@ -574,11 +585,12 @@ impl LightSync { if requested_from.contains(peer) { continue } match ctx.request_from(*peer, request.clone()) { Ok(id) => { - let timeout_ms = REQ_TIMEOUT_MILLISECS_BASE + - req.max * REQ_TIMEOUT_MILLISECS_PER_HEADER; + assert!(req.max <= u32::max_value() as u64, + "requesting more than 2^32 headers at a time would overflow"); + let timeout = REQ_TIMEOUT_BASE + REQ_TIMEOUT_PER_HEADER * req.max as u32; self.pending_reqs.lock().insert(id.clone(), PendingReq { started: Instant::now(), - timeout: Duration::from_millis(timeout_ms), + timeout, }); requested_from.insert(peer.clone()); diff --git a/sync/src/light_sync/response.rs b/ethcore/sync/src/light_sync/response.rs similarity index 91% rename from sync/src/light_sync/response.rs rename to ethcore/sync/src/light_sync/response.rs index 4dfb383d4668a03f29c1f0e61252a550f20bffae..161461c2a5978509238a797ff9e7c4fe4782c160 100644 --- a/sync/src/light_sync/response.rs +++ b/ethcore/sync/src/light_sync/response.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -16,13 +16,11 @@ //! Helpers for decoding and verifying responses for headers. -use std::fmt; - -use ethcore::encoded; -use ethcore::header::Header; +use ethcore::{encoded, header::Header}; +use ethereum_types::H256; use light::request::{HashOrNumber, CompleteHeadersRequest as HeadersRequest}; use rlp::DecoderError; -use ethereum_types::H256; +use std::fmt; /// Errors found when decoding headers and verifying with basic constraints. #[derive(Debug, PartialEq)] @@ -74,19 +72,23 @@ pub trait Constraint { /// Do basic verification of provided headers against a request. pub fn verify(headers: &[encoded::Header], request: &HeadersRequest) -> Result, BasicError> { - let headers: Vec<_> = headers.iter().map(|h| h.decode()).collect(); + let headers: Result, _> = headers.iter().map(|h| h.decode() ).collect(); + match headers { + Ok(headers) => { + let reverse = request.reverse; + + Max(request.max as usize).verify(&headers, reverse)?; + match request.start { + HashOrNumber::Number(ref num) => StartsAtNumber(*num).verify(&headers, reverse)?, + HashOrNumber::Hash(ref hash) => StartsAtHash(*hash).verify(&headers, reverse)?, + } - let reverse = request.reverse; + SkipsBetween(request.skip).verify(&headers, reverse)?; - Max(request.max as usize).verify(&headers, reverse)?; - match request.start { - HashOrNumber::Number(ref num) => StartsAtNumber(*num).verify(&headers, reverse)?, - HashOrNumber::Hash(ref hash) => StartsAtHash(*hash).verify(&headers, reverse)?, + Ok(headers) + }, + Err(e) => Err(e.into()) } - - SkipsBetween(request.skip).verify(&headers, reverse)?; - - Ok(headers) } struct StartsAtNumber(u64); diff --git a/sync/src/light_sync/sync_round.rs b/ethcore/sync/src/light_sync/sync_round.rs similarity index 99% rename from sync/src/light_sync/sync_round.rs rename to ethcore/sync/src/light_sync/sync_round.rs index d477ecc81503f074d9cce3993b116502fb4e4d04..79684efe53dad9f233b052b5bca8e7fb3c7bdf71 100644 --- a/sync/src/light_sync/sync_round.rs +++ b/ethcore/sync/src/light_sync/sync_round.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/sync/src/light_sync/tests/mod.rs b/ethcore/sync/src/light_sync/tests/mod.rs similarity index 96% rename from sync/src/light_sync/tests/mod.rs rename to ethcore/sync/src/light_sync/tests/mod.rs index 9fd270838bf7bb0aee8e042f47052c8606152aca..e3d46188a65bf634c93fbcde6815336e2039bf9b 100644 --- a/sync/src/light_sync/tests/mod.rs +++ b/ethcore/sync/src/light_sync/tests/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -45,7 +45,7 @@ fn fork_post_cht() { for id in (0..CHAIN_LENGTH).map(|x| x + 1).map(BlockId::Number) { let (light_peer, full_peer) = (net.peer(0), net.peer(1)); let light_chain = light_peer.light_chain(); - let header = full_peer.chain().block_header(id).unwrap().decode(); + let header = full_peer.chain().block_header(id).unwrap().decode().expect("decoding failure"); let _ = light_chain.import_header(header); light_chain.flush_queue(); light_chain.import_verified(); diff --git a/sync/src/light_sync/tests/test_net.rs b/ethcore/sync/src/light_sync/tests/test_net.rs similarity index 97% rename from sync/src/light_sync/tests/test_net.rs rename to ethcore/sync/src/light_sync/tests/test_net.rs index f935680573f0e144d9394c7fd112e6d2210296ab..5995bd7c6c0b94309e166a50c45c096ecd2831d6 100644 --- a/sync/src/light_sync/tests/test_net.rs +++ b/ethcore/sync/src/light_sync/tests/test_net.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -205,6 +205,10 @@ impl PeerLike for Peer { } fn restart_sync(&self) { } + + fn process_all_io_messages(&self) { } + + fn process_all_new_block_messages(&self) { } } impl TestNet { diff --git a/ethcore/sync/src/private_tx.rs b/ethcore/sync/src/private_tx.rs new file mode 100644 index 0000000000000000000000000000000000000000..d7434c8bd5b13c7dc27b6aec4112f7f5842c6b01 --- /dev/null +++ b/ethcore/sync/src/private_tx.rs @@ -0,0 +1,60 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use parking_lot::Mutex; + +/// Trait which should be implemented by a private transaction handler. +pub trait PrivateTxHandler: Send + Sync + 'static { + /// Function called on new private transaction received. + fn import_private_transaction(&self, rlp: &[u8]) -> Result<(), String>; + + /// Function called on new signed private transaction received. + fn import_signed_private_transaction(&self, rlp: &[u8]) -> Result<(), String>; +} + +/// Nonoperative private transaction handler. +pub struct NoopPrivateTxHandler; + +impl PrivateTxHandler for NoopPrivateTxHandler { + fn import_private_transaction(&self, _rlp: &[u8]) -> Result<(), String> { + Ok(()) + } + + fn import_signed_private_transaction(&self, _rlp: &[u8]) -> Result<(), String> { + Ok(()) + } +} + +/// Simple private transaction handler. Used for tests. +#[derive(Default)] +pub struct SimplePrivateTxHandler { + /// imported private transactions + pub txs: Mutex>>, + /// imported signed private transactions + pub signed_txs: Mutex>>, +} + +impl PrivateTxHandler for SimplePrivateTxHandler { + fn import_private_transaction(&self, rlp: &[u8]) -> Result<(), String> { + self.txs.lock().push(rlp.to_vec()); + Ok(()) + } + + fn import_signed_private_transaction(&self, rlp: &[u8]) -> Result<(), String> { + self.signed_txs.lock().push(rlp.to_vec()); + Ok(()) + } +} diff --git a/ethcore/sync/src/res/private_spec.json b/ethcore/sync/src/res/private_spec.json new file mode 100644 index 0000000000000000000000000000000000000000..f93d754a6111617527a354fe0ee0704537ab6d4c --- /dev/null +++ b/ethcore/sync/src/res/private_spec.json @@ -0,0 +1,30 @@ +{ + "name": "PrivateTransactions", + "engine": { + "instantSeal": null + }, + "params": { + "gasLimitBoundDivisor": "0x0400", + "accountStartNonce": "0x0", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "networkID" : "0x11" + }, + "genesis": { + "seal": { + "generic": "0x0" + }, + "difficulty": "0x20000", + "author": "0x0000000000000000000000000000000000000000", + "timestamp": "0x00", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x", + "gasLimit": "0x989680" + }, + "accounts": { + "0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, + "0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, + "0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, + "0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } } + } +} \ No newline at end of file diff --git a/sync/src/snapshot.rs b/ethcore/sync/src/snapshot.rs similarity index 78% rename from sync/src/snapshot.rs rename to ethcore/sync/src/snapshot.rs index 917e84c8846e41afaf10243d7339ae373d684e9d..e5632e652b84111c542e27e6c0b225684c6c212e 100644 --- a/sync/src/snapshot.rs +++ b/ethcore/sync/src/snapshot.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -14,10 +14,13 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use hash::keccak; +use ethcore::snapshot::{ManifestData, SnapshotService}; use ethereum_types::H256; +use hash::keccak; +use rand::{thread_rng, Rng}; + use std::collections::HashSet; -use ethcore::snapshot::ManifestData; +use std::iter::FromIterator; #[derive(PartialEq, Eq, Debug)] pub enum ChunkType { @@ -32,6 +35,7 @@ pub struct Snapshot { completed_chunks: HashSet, snapshot_hash: Option, bad_hashes: HashSet, + initialized: bool, } impl Snapshot { @@ -44,9 +48,29 @@ impl Snapshot { completed_chunks: HashSet::new(), snapshot_hash: None, bad_hashes: HashSet::new(), + initialized: false, } } + /// Sync the Snapshot completed chunks with the Snapshot Service + pub fn initialize(&mut self, snapshot_service: &SnapshotService) { + if self.initialized { + return; + } + + if let Some(completed_chunks) = snapshot_service.completed_chunks() { + self.completed_chunks = HashSet::from_iter(completed_chunks); + } + + trace!( + target: "snapshot", + "Snapshot is now initialized with {} completed chunks.", + self.completed_chunks.len(), + ); + + self.initialized = true; + } + /// Clear everything. pub fn clear(&mut self) { self.pending_state_chunks.clear(); @@ -54,6 +78,7 @@ impl Snapshot { self.downloading_chunks.clear(); self.completed_chunks.clear(); self.snapshot_hash = None; + self.initialized = false; } /// Check if currently downloading a snapshot. @@ -89,18 +114,35 @@ impl Snapshot { Err(()) } - /// Find a chunk to download + /// Find a random chunk to download pub fn needed_chunk(&mut self) -> Option { - // check state chunks first - let chunk = self.pending_state_chunks.iter() - .chain(self.pending_block_chunks.iter()) - .find(|&h| !self.downloading_chunks.contains(h) && !self.completed_chunks.contains(h)) - .cloned(); + // Find all random chunks: first blocks, then state + let needed_chunks = { + let chunk_filter = |h| !self.downloading_chunks.contains(h) && !self.completed_chunks.contains(h); + + let needed_block_chunks = self.pending_block_chunks.iter() + .filter(|&h| chunk_filter(h)) + .map(|h| *h) + .collect::>(); + + // If no block chunks to download, get the state chunks + if needed_block_chunks.len() == 0 { + self.pending_state_chunks.iter() + .filter(|&h| chunk_filter(h)) + .map(|h| *h) + .collect::>() + } else { + needed_block_chunks + } + }; + + // Get a random chunk + let chunk = thread_rng().choose(&needed_chunks); if let Some(hash) = chunk { self.downloading_chunks.insert(hash.clone()); } - chunk + chunk.map(|h| *h) } pub fn clear_chunk_download(&mut self, hash: &H256) { @@ -185,8 +227,15 @@ mod test { let requested: Vec = (0..40).map(|_| snapshot.needed_chunk().unwrap()).collect(); assert!(snapshot.needed_chunk().is_none()); - assert_eq!(&requested[0..20], &manifest.state_hashes[..]); - assert_eq!(&requested[20..40], &manifest.block_hashes[..]); + + let requested_all_block_chunks = manifest.block_hashes.iter() + .all(|h| requested.iter().any(|rh| rh == h)); + assert!(requested_all_block_chunks); + + let requested_all_state_chunks = manifest.state_hashes.iter() + .all(|h| requested.iter().any(|rh| rh == h)); + assert!(requested_all_state_chunks); + assert_eq!(snapshot.downloading_chunks.len(), 40); assert_eq!(snapshot.validate_chunk(&state_chunks[4]), Ok(ChunkType::State(manifest.state_hashes[4].clone()))); @@ -225,4 +274,3 @@ mod test { assert_eq!(snapshot.is_known_bad(&hash), true); } } - diff --git a/sync/src/sync_io.rs b/ethcore/sync/src/sync_io.rs similarity index 98% rename from sync/src/sync_io.rs rename to ethcore/sync/src/sync_io.rs index 76f323e82614151344a67786c4bdad18d0a3e42c..c7704724c6625f954b3578373c22f273d92d1163 100644 --- a/sync/src/sync_io.rs +++ b/ethcore/sync/src/sync_io.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -136,5 +136,3 @@ impl<'s> SyncIo for NetSyncIo<'s> { self.network.peer_client_version(peer_id) } } - - diff --git a/sync/src/tests/chain.rs b/ethcore/sync/src/tests/chain.rs similarity index 98% rename from sync/src/tests/chain.rs rename to ethcore/sync/src/tests/chain.rs index 517ff0d99a4dc603e3928cf69ce21bfe7a20545e..0d9c83f2fb49b27ec746b40f65b750ef3931dde5 100644 --- a/sync/src/tests/chain.rs +++ b/ethcore/sync/src/tests/chain.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -18,7 +18,7 @@ use std::sync::Arc; use ethcore::client::{TestBlockChainClient, BlockChainClient, BlockId, EachBlockWith, ChainInfo, BlockInfo}; use chain::{SyncState}; use super::helpers::*; -use SyncConfig; +use {SyncConfig, WarpSync}; #[test] fn two_peers() { @@ -161,7 +161,7 @@ fn status_empty() { let net = TestNet::new(2); assert_eq!(net.peer(0).sync.read().status().state, SyncState::Idle); let mut config = SyncConfig::default(); - config.warp_sync = true; + config.warp_sync = WarpSync::Enabled; let net = TestNet::new_with_config(2, config); assert_eq!(net.peer(0).sync.read().status().state, SyncState::WaitingPeers); } @@ -253,7 +253,6 @@ fn high_td_attach() { assert_eq!(net.peer(0).chain.chain_info().best_block_number, 5); } - #[test] fn disconnect_on_unrelated_chain() { ::env_logger::init().ok(); @@ -267,4 +266,3 @@ fn disconnect_on_unrelated_chain() { net.sync(); assert_eq!(net.disconnect_events, vec![(0, 0)]); } - diff --git a/sync/src/tests/consensus.rs b/ethcore/sync/src/tests/consensus.rs similarity index 70% rename from sync/src/tests/consensus.rs rename to ethcore/sync/src/tests/consensus.rs index 951c027afe8c5c775777828a57f33bb980cb3e42..6b2502f4a682160bce3841828351ddd4fcf0c075 100644 --- a/sync/src/tests/consensus.rs +++ b/ethcore/sync/src/tests/consensus.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -17,8 +17,8 @@ use std::sync::Arc; use hash::keccak; use ethereum_types::{U256, Address}; -use io::{IoHandler, IoContext, IoChannel}; -use ethcore::client::{Client, ChainInfo, ClientIoMessage}; +use io::{IoHandler, IoChannel}; +use ethcore::client::{ChainInfo, ClientIoMessage}; use ethcore::spec::Spec; use ethcore::miner::MinerService; use ethcore::account_provider::AccountProvider; @@ -27,21 +27,6 @@ use transaction::{Action, PendingTransaction, Transaction}; use super::helpers::*; use SyncConfig; -struct TestIoHandler { - client: Arc, -} - -impl IoHandler for TestIoHandler { - fn message(&self, _io: &IoContext, net_message: &ClientIoMessage) { - match *net_message { - ClientIoMessage::NewMessage(ref message) => if let Err(e) = self.client.engine().handle_message(message) { - panic!("Invalid message received: {}", e); - }, - _ => {} // ignore other messages - } - } -} - fn new_tx(secret: &Secret, nonce: U256, chain_id: u64) -> PendingTransaction { let signed = Transaction { nonce: nonce.into(), @@ -64,11 +49,11 @@ fn authority_round() { let chain_id = Spec::new_test_round().chain_id(); let mut net = TestNet::with_spec_and_accounts(2, SyncConfig::default(), Spec::new_test_round, Some(ap)); - let io_handler0: Arc> = Arc::new(TestIoHandler { client: net.peer(0).chain.clone() }); - let io_handler1: Arc> = Arc::new(TestIoHandler { client: net.peer(1).chain.clone() }); + let io_handler0: Arc> = Arc::new(TestIoHandler::new(net.peer(0).chain.clone())); + let io_handler1: Arc> = Arc::new(TestIoHandler::new(net.peer(1).chain.clone())); // Push transaction to both clients. Only one of them gets lucky to produce a block. - net.peer(0).chain.miner().set_engine_signer(s0.address(), "".to_owned()).unwrap(); - net.peer(1).chain.miner().set_engine_signer(s1.address(), "".to_owned()).unwrap(); + net.peer(0).miner.set_author(s0.address(), Some("".into())).unwrap(); + net.peer(1).miner.set_author(s1.address(), Some("".to_owned())).unwrap(); net.peer(0).chain.engine().register_client(Arc::downgrade(&net.peer(0).chain) as _); net.peer(1).chain.engine().register_client(Arc::downgrade(&net.peer(1).chain) as _); net.peer(0).chain.set_io_channel(IoChannel::to_handler(Arc::downgrade(&io_handler1))); @@ -76,15 +61,15 @@ fn authority_round() { // exchange statuses net.sync(); // Trigger block proposal - net.peer(0).chain.miner().import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 0.into(), chain_id)).unwrap(); - net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 0.into(), chain_id)).unwrap(); + net.peer(0).miner.import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 0.into(), chain_id)).unwrap(); + net.peer(1).miner.import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 0.into(), chain_id)).unwrap(); // Sync a block net.sync(); assert_eq!(net.peer(0).chain.chain_info().best_block_number, 1); assert_eq!(net.peer(1).chain.chain_info().best_block_number, 1); - net.peer(0).chain.miner().import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 1.into(), chain_id)).unwrap(); - net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 1.into(), chain_id)).unwrap(); + net.peer(0).miner.import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 1.into(), chain_id)).unwrap(); + net.peer(1).miner.import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 1.into(), chain_id)).unwrap(); // Move to next proposer step. net.peer(0).chain.engine().step(); net.peer(1).chain.engine().step(); @@ -93,8 +78,8 @@ fn authority_round() { assert_eq!(net.peer(1).chain.chain_info().best_block_number, 2); // Fork the network with equal height. - net.peer(0).chain.miner().import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 2.into(), chain_id)).unwrap(); - net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 2.into(), chain_id)).unwrap(); + net.peer(0).miner.import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 2.into(), chain_id)).unwrap(); + net.peer(1).miner.import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 2.into(), chain_id)).unwrap(); // Let both nodes build one block. net.peer(0).chain.engine().step(); let early_hash = net.peer(0).chain.chain_info().best_block_hash; @@ -116,8 +101,8 @@ fn authority_round() { assert_eq!(ci1.best_block_hash, early_hash); // Selfish miner - net.peer(0).chain.miner().import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 3.into(), chain_id)).unwrap(); - net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 3.into(), chain_id)).unwrap(); + net.peer(0).miner.import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 3.into(), chain_id)).unwrap(); + net.peer(1).miner.import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 3.into(), chain_id)).unwrap(); // Node 0 is an earlier primary. net.peer(0).chain.engine().step(); assert_eq!(net.peer(0).chain.chain_info().best_block_number, 4); @@ -128,7 +113,7 @@ fn authority_round() { // Node 1 makes 2 blocks, but is a later primary on the first one. net.peer(1).chain.engine().step(); net.peer(1).chain.engine().step(); - net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 4.into(), chain_id)).unwrap(); + net.peer(1).miner.import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 4.into(), chain_id)).unwrap(); net.peer(1).chain.engine().step(); net.peer(1).chain.engine().step(); assert_eq!(net.peer(1).chain.chain_info().best_block_number, 5); @@ -151,12 +136,12 @@ fn tendermint() { let chain_id = Spec::new_test_tendermint().chain_id(); let mut net = TestNet::with_spec_and_accounts(2, SyncConfig::default(), Spec::new_test_tendermint, Some(ap)); - let io_handler0: Arc> = Arc::new(TestIoHandler { client: net.peer(0).chain.clone() }); - let io_handler1: Arc> = Arc::new(TestIoHandler { client: net.peer(1).chain.clone() }); + let io_handler0: Arc> = Arc::new(TestIoHandler::new(net.peer(0).chain.clone())); + let io_handler1: Arc> = Arc::new(TestIoHandler::new(net.peer(1).chain.clone())); // Push transaction to both clients. Only one of them issues a proposal. - net.peer(0).chain.miner().set_engine_signer(s0.address(), "".to_owned()).unwrap(); + net.peer(0).miner.set_author(s0.address(), Some("".into())).unwrap(); trace!(target: "poa", "Peer 0 is {}.", s0.address()); - net.peer(1).chain.miner().set_engine_signer(s1.address(), "".to_owned()).unwrap(); + net.peer(1).miner.set_author(s1.address(), Some("".into())).unwrap(); trace!(target: "poa", "Peer 1 is {}.", s1.address()); net.peer(0).chain.engine().register_client(Arc::downgrade(&net.peer(0).chain) as _); net.peer(1).chain.engine().register_client(Arc::downgrade(&net.peer(1).chain) as _); @@ -165,7 +150,7 @@ fn tendermint() { // Exhange statuses net.sync(); // Propose - net.peer(0).chain.miner().import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 0.into(), chain_id)).unwrap(); + net.peer(0).miner.import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 0.into(), chain_id)).unwrap(); net.sync(); // Propose timeout, synchronous for now net.peer(0).chain.engine().step(); @@ -176,7 +161,7 @@ fn tendermint() { assert_eq!(net.peer(0).chain.chain_info().best_block_number, 1); assert_eq!(net.peer(1).chain.chain_info().best_block_number, 1); - net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 0.into(), chain_id)).unwrap(); + net.peer(1).miner.import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 0.into(), chain_id)).unwrap(); // Commit timeout net.peer(0).chain.engine().step(); net.peer(1).chain.engine().step(); @@ -190,8 +175,8 @@ fn tendermint() { assert_eq!(net.peer(0).chain.chain_info().best_block_number, 2); assert_eq!(net.peer(1).chain.chain_info().best_block_number, 2); - net.peer(0).chain.miner().import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 1.into(), chain_id)).unwrap(); - net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 1.into(), chain_id)).unwrap(); + net.peer(0).miner.import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 1.into(), chain_id)).unwrap(); + net.peer(1).miner.import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 1.into(), chain_id)).unwrap(); // Peers get disconnected. // Commit net.peer(0).chain.engine().step(); @@ -199,8 +184,8 @@ fn tendermint() { // Propose net.peer(0).chain.engine().step(); net.peer(1).chain.engine().step(); - net.peer(0).chain.miner().import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 2.into(), chain_id)).unwrap(); - net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 2.into(), chain_id)).unwrap(); + net.peer(0).miner.import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 2.into(), chain_id)).unwrap(); + net.peer(1).miner.import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 2.into(), chain_id)).unwrap(); // Send different prevotes net.sync(); // Prevote timeout diff --git a/sync/src/tests/helpers.rs b/ethcore/sync/src/tests/helpers.rs similarity index 68% rename from sync/src/tests/helpers.rs rename to ethcore/sync/src/tests/helpers.rs index 32a90b414fe28f7e0b39934744c1b7ed439a2176..112dab8a986af7da3372a04125b80e935c154341 100644 --- a/sync/src/tests/helpers.rs +++ b/ethcore/sync/src/tests/helpers.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -16,22 +16,25 @@ use std::collections::{VecDeque, HashSet, HashMap}; use std::sync::Arc; +use std::time::Duration; use ethereum_types::H256; -use parking_lot::RwLock; +use parking_lot::{RwLock, Mutex}; use bytes::Bytes; use network::{self, PeerId, ProtocolId, PacketId, SessionInfo}; use tests::snapshot::*; -use ethcore::client::{TestBlockChainClient, BlockChainClient, Client as EthcoreClient, ClientConfig, ChainNotify}; +use ethcore::client::{TestBlockChainClient, BlockChainClient, Client as EthcoreClient, + ClientConfig, ChainNotify, ChainRoute, ChainMessageType, ClientIoMessage}; use ethcore::header::BlockNumber; use ethcore::snapshot::SnapshotService; use ethcore::spec::Spec; use ethcore::account_provider::AccountProvider; use ethcore::miner::Miner; use sync_io::SyncIo; -use io::IoChannel; +use io::{IoChannel, IoContext, IoHandler}; use api::WARP_SYNC_PROTOCOL_ID; -use chain::ChainSync; -use ::SyncConfig; +use chain::{ChainSync, ETH_PROTOCOL_VERSION_63, PAR_PROTOCOL_VERSION_3}; +use SyncConfig; +use private_tx::SimplePrivateTxHandler; pub trait FlushingBlockChainClient: BlockChainClient { fn flush(&self) {} @@ -131,11 +134,11 @@ impl<'p, C> SyncIo for TestIo<'p, C> where C: FlushingBlockChainClient, C: 'p { } fn eth_protocol_version(&self, _peer: PeerId) -> u8 { - 63 + ETH_PROTOCOL_VERSION_63.0 } fn protocol_version(&self, protocol: &ProtocolId, peer_id: PeerId) -> u8 { - if protocol == &WARP_SYNC_PROTOCOL_ID { 2 } else { self.eth_protocol_version(peer_id) } + if protocol == &WARP_SYNC_PROTOCOL_ID { PAR_PROTOCOL_VERSION_3.0 } else { self.eth_protocol_version(peer_id) } } fn chain_overlay(&self) -> &RwLock> { @@ -143,6 +146,16 @@ impl<'p, C> SyncIo for TestIo<'p, C> where C: FlushingBlockChainClient, C: 'p { } } +/// Mock for emulution of async run of new blocks +struct NewBlockMessage { + imported: Vec, + invalid: Vec, + enacted: Vec, + retracted: Vec, + sealed: Vec, + proposed: Vec, +} + /// Abstract messages between peers. pub trait Message { /// The intended recipient of this message. @@ -184,13 +197,55 @@ pub trait Peer { /// Restart sync for a peer. fn restart_sync(&self); + + /// Process the queue of pending io messages + fn process_all_io_messages(&self); + + /// Process the queue of new block messages + fn process_all_new_block_messages(&self); } pub struct EthPeer where C: FlushingBlockChainClient { pub chain: Arc, + pub miner: Arc, pub snapshot_service: Arc, pub sync: RwLock, pub queue: RwLock>, + pub private_tx_handler: Arc, + pub io_queue: RwLock>, + new_blocks_queue: RwLock>, +} + +impl EthPeer where C: FlushingBlockChainClient { + fn is_io_queue_empty(&self) -> bool { + self.io_queue.read().is_empty() + } + + fn is_new_blocks_queue_empty(&self) -> bool { + self.new_blocks_queue.read().is_empty() + } + + fn process_io_message(&self, message: ChainMessageType) { + let mut io = TestIo::new(&*self.chain, &self.snapshot_service, &self.queue, None); + match message { + ChainMessageType::Consensus(data) => self.sync.write().propagate_consensus_packet(&mut io, data), + ChainMessageType::PrivateTransaction(data) => self.sync.write().propagate_private_transaction(&mut io, data), + ChainMessageType::SignedPrivateTransaction(data) => self.sync.write().propagate_signed_private_transaction(&mut io, data), + } + } + + fn process_new_block_message(&self, message: NewBlockMessage) { + let mut io = TestIo::new(&*self.chain, &self.snapshot_service, &self.queue, None); + self.sync.write().chain_new_blocks( + &mut io, + &message.imported, + &message.invalid, + &message.enacted, + &message.retracted, + &message.sealed, + &message.proposed + ); + } } impl Peer for EthPeer { @@ -198,7 +253,12 @@ impl Peer for EthPeer { fn on_connect(&self, other: PeerId) { self.sync.write().update_targets(&*self.chain); - self.sync.write().on_peer_connected(&mut TestIo::new(&*self.chain, &self.snapshot_service, &self.queue, Some(other)), other); + self.sync.write().on_peer_connected(&mut TestIo::new( + &*self.chain, + &self.snapshot_service, + &self.queue, + Some(other)), + other); } fn on_disconnect(&self, other: PeerId) { @@ -219,7 +279,7 @@ impl Peer for EthPeer { } fn is_done(&self) -> bool { - self.queue.read().is_empty() + self.queue.read().is_empty() && self.is_io_queue_empty() && self.is_new_blocks_queue_empty() } fn sync_step(&self) { @@ -232,6 +292,22 @@ impl Peer for EthPeer { fn restart_sync(&self) { self.sync.write().restart(&mut TestIo::new(&*self.chain, &self.snapshot_service, &self.queue, None)); } + + fn process_all_io_messages(&self) { + if !self.is_io_queue_empty() { + while let Some(message) = self.io_queue.write().pop_front() { + self.process_io_message(message); + } + } + } + + fn process_all_new_block_messages(&self) { + if !self.is_new_blocks_queue_empty() { + while let Some(message) = self.new_blocks_queue.write().pop_front() { + self.process_new_block_message(message); + } + } + } } pub struct TestNet

{ @@ -260,20 +336,35 @@ impl TestNet> { for _ in 0..n { let chain = TestBlockChainClient::new(); let ss = Arc::new(TestSnapshotService::new()); - let sync = ChainSync::new(config.clone(), &chain); + let private_tx_handler = Arc::new(SimplePrivateTxHandler::default()); + let sync = ChainSync::new(config.clone(), &chain, private_tx_handler.clone()); net.peers.push(Arc::new(EthPeer { sync: RwLock::new(sync), snapshot_service: ss, chain: Arc::new(chain), + miner: Arc::new(Miner::new_for_tests(&Spec::new_test(), None)), queue: RwLock::new(VecDeque::new()), + private_tx_handler, + io_queue: RwLock::new(VecDeque::new()), + new_blocks_queue: RwLock::new(VecDeque::new()), })); } net } + + // relies on Arc uniqueness, which is only true when we haven't registered a ChainNotify. + pub fn peer_mut(&mut self, i: usize) -> &mut EthPeer { + Arc::get_mut(&mut self.peers[i]).expect("Arc never exposed externally") + } } impl TestNet> { - pub fn with_spec_and_accounts(n: usize, config: SyncConfig, spec_factory: F, accounts: Option>) -> Self + pub fn with_spec_and_accounts( + n: usize, + config: SyncConfig, + spec_factory: F, + accounts: Option> + ) -> Self where F: Fn() -> Spec { let mut net = TestNet { @@ -282,29 +373,37 @@ impl TestNet> { disconnect_events: Vec::new(), }; for _ in 0..n { - net.add_peer(config.clone(), spec_factory(), accounts.clone()); + net.add_peer_with_private_config(config.clone(), spec_factory(), accounts.clone()); } net } - pub fn add_peer(&mut self, config: SyncConfig, spec: Spec, accounts: Option>) { + pub fn add_peer_with_private_config(&mut self, config: SyncConfig, spec: Spec, accounts: Option>) { + let channel = IoChannel::disconnected(); + let miner = Arc::new(Miner::new_for_tests(&spec, accounts.clone())); let client = EthcoreClient::new( ClientConfig::default(), &spec, Arc::new(::kvdb_memorydb::create(::ethcore::db::NUM_COLUMNS.unwrap_or(0))), - Arc::new(Miner::with_spec_and_accounts(&spec, accounts)), - IoChannel::disconnected(), + miner.clone(), + channel.clone() ).unwrap(); + let private_tx_handler = Arc::new(SimplePrivateTxHandler::default()); let ss = Arc::new(TestSnapshotService::new()); - let sync = ChainSync::new(config, &*client); + let sync = ChainSync::new(config, &*client, private_tx_handler.clone()); let peer = Arc::new(EthPeer { sync: RwLock::new(sync), snapshot_service: ss, chain: client, + miner, queue: RwLock::new(VecDeque::new()), + private_tx_handler, + io_queue: RwLock::new(VecDeque::new()), + new_blocks_queue: RwLock::new(VecDeque::new()), }); peer.chain.add_notify(peer.clone()); + //private_provider.add_notify(peer.clone()); self.peers.push(peer); } } @@ -366,6 +465,8 @@ impl

TestNet

where P: Peer { let mut total_steps = 0; while !self.done() { self.sync_step(); + self.deliver_io_messages(); + self.deliver_new_block_messages(); total_steps += 1; } total_steps @@ -378,15 +479,20 @@ impl

TestNet

where P: Peer { } } - pub fn done(&self) -> bool { - self.peers.iter().all(|p| p.is_done()) + pub fn deliver_io_messages(&mut self) { + for peer in self.peers.iter() { + peer.process_all_io_messages(); + } } -} -impl TestNet> { - // relies on Arc uniqueness, which is only true when we haven't registered a ChainNotify. - pub fn peer_mut(&mut self, i: usize) -> &mut EthPeer { - Arc::get_mut(&mut self.peers[i]).expect("Arc never exposed externally") + pub fn deliver_new_block_messages(&mut self) { + for peer in self.peers.iter() { + peer.process_all_new_block_messages(); + } + } + + pub fn done(&self) -> bool { + self.peers.iter().all(|p| p.is_done()) } } @@ -397,33 +503,58 @@ impl TestNet> { } } +pub struct TestIoHandler { + pub client: Arc, + pub private_tx_queued: Mutex, +} + +impl TestIoHandler { + pub fn new(client: Arc) -> Self { + TestIoHandler { + client, + private_tx_queued: Mutex::default(), + } + } +} + +impl IoHandler for TestIoHandler { + fn message(&self, _io: &IoContext, net_message: &ClientIoMessage) { + match *net_message { + ClientIoMessage::Execute(ref exec) => { + *self.private_tx_queued.lock() += 1; + (*exec.0)(&self.client); + }, + _ => {} // ignore other messages + } + } +} + impl ChainNotify for EthPeer { fn new_blocks(&self, imported: Vec, invalid: Vec, - enacted: Vec, - retracted: Vec, + route: ChainRoute, sealed: Vec, proposed: Vec, - _duration: u64) + _duration: Duration) { - let mut io = TestIo::new(&*self.chain, &self.snapshot_service, &self.queue, None); - self.sync.write().chain_new_blocks( - &mut io, - &imported, - &invalid, - &enacted, - &retracted, - &sealed, - &proposed); + let (enacted, retracted) = route.into_enacted_retracted(); + + self.new_blocks_queue.write().push_back(NewBlockMessage { + imported, + invalid, + enacted, + retracted, + sealed, + proposed, + }); } fn start(&self) {} fn stop(&self) {} - fn broadcast(&self, message: Vec) { - let mut io = TestIo::new(&*self.chain, &self.snapshot_service, &self.queue, None); - self.sync.write().propagate_consensus_packet(&mut io, message.clone()); + fn broadcast(&self, message_type: ChainMessageType) { + self.io_queue.write().push_back(message_type) } } diff --git a/sync/src/tests/mod.rs b/ethcore/sync/src/tests/mod.rs similarity index 91% rename from sync/src/tests/mod.rs rename to ethcore/sync/src/tests/mod.rs index 8b9059fc135c553d9c46f5cf9d07193a839437c1..0168913aa1655c398054a8de0532bfd4c07d8c0d 100644 --- a/sync/src/tests/mod.rs +++ b/ethcore/sync/src/tests/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -18,6 +18,7 @@ pub mod helpers; pub mod snapshot; mod chain; mod consensus; +mod private; #[cfg(feature = "ipc")] mod rpc; diff --git a/ethcore/sync/src/tests/private.rs b/ethcore/sync/src/tests/private.rs new file mode 100644 index 0000000000000000000000000000000000000000..120dc8fc9c50981600883431cd9da42657821018 --- /dev/null +++ b/ethcore/sync/src/tests/private.rs @@ -0,0 +1,149 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use std::sync::Arc; +use hash::keccak; +use io::{IoHandler, IoChannel}; +use ethcore::client::{BlockChainClient, BlockId, ClientIoMessage}; +use ethcore::spec::Spec; +use ethcore::miner::MinerService; +use ethcore::CreateContractAddress; +use transaction::{Transaction, Action}; +use ethcore::executive::{contract_address}; +use ethcore::test_helpers::{push_block_with_transactions}; +use ethcore_private_tx::{Provider, ProviderConfig, NoopEncryptor, Importer}; +use ethcore::account_provider::AccountProvider; +use ethkey::{KeyPair}; +use tests::helpers::{TestNet, TestIoHandler}; +use rustc_hex::FromHex; +use SyncConfig; + +fn seal_spec() -> Spec { + let spec_data = include_str!("../res/private_spec.json"); + Spec::load(&::std::env::temp_dir(), spec_data.as_bytes()).unwrap() +} + +#[test] +fn send_private_transaction() { + // Setup two clients + let s0 = KeyPair::from_secret_slice(&keccak("1")).unwrap(); + let s1 = KeyPair::from_secret_slice(&keccak("0")).unwrap(); + let ap = Arc::new(AccountProvider::transient_provider()); + ap.insert_account(s0.secret().clone(), "").unwrap(); + ap.insert_account(s1.secret().clone(), "").unwrap(); + + let mut net = TestNet::with_spec_and_accounts(2, SyncConfig::default(), seal_spec, Some(ap.clone())); + let client0 = net.peer(0).chain.clone(); + let client1 = net.peer(1).chain.clone(); + let io_handler0: Arc> = Arc::new(TestIoHandler::new(net.peer(0).chain.clone())); + let io_handler1: Arc> = Arc::new(TestIoHandler::new(net.peer(1).chain.clone())); + + net.peer(0).miner.set_author(s0.address(), Some("".into())).unwrap(); + net.peer(1).miner.set_author(s1.address(), Some("".to_owned())).unwrap(); + net.peer(0).chain.engine().register_client(Arc::downgrade(&net.peer(0).chain) as _); + net.peer(1).chain.engine().register_client(Arc::downgrade(&net.peer(1).chain) as _); + net.peer(0).chain.set_io_channel(IoChannel::to_handler(Arc::downgrade(&io_handler0))); + net.peer(1).chain.set_io_channel(IoChannel::to_handler(Arc::downgrade(&io_handler1))); + + let (address, _) = contract_address(CreateContractAddress::FromSenderAndNonce, &s0.address(), &0.into(), &[]); + let chain_id = client0.signing_chain_id(); + + // Exhange statuses + net.sync(); + + // Setup private providers + let validator_config = ProviderConfig{ + validator_accounts: vec![s1.address()], + signer_account: None, + passwords: vec!["".into()], + }; + + let signer_config = ProviderConfig{ + validator_accounts: Vec::new(), + signer_account: Some(s0.address()), + passwords: vec!["".into()], + }; + + let pm0 = Arc::new(Provider::new( + client0.clone(), + net.peer(0).miner.clone(), + ap.clone(), + Box::new(NoopEncryptor::default()), + signer_config, + IoChannel::to_handler(Arc::downgrade(&io_handler0)), + )); + pm0.add_notify(net.peers[0].clone()); + + let pm1 = Arc::new(Provider::new( + client1.clone(), + net.peer(1).miner.clone(), + ap.clone(), + Box::new(NoopEncryptor::default()), + validator_config, + IoChannel::to_handler(Arc::downgrade(&io_handler1)), + )); + pm1.add_notify(net.peers[1].clone()); + + // Create and deploy contract + let private_contract_test = "6060604052341561000f57600080fd5b60d88061001d6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630c55699c146046578063bc64b76d14607457600080fd5b3415605057600080fd5b60566098565b60405180826000191660001916815260200191505060405180910390f35b3415607e57600080fd5b6096600480803560001916906020019091905050609e565b005b60005481565b8060008160001916905550505600a165627a7a723058206acbdf4b15ca4c2d43e1b1879b830451a34f1e9d02ff1f2f394d8d857e79d2080029".from_hex().unwrap(); + let mut private_create_tx = Transaction::default(); + private_create_tx.action = Action::Create; + private_create_tx.data = private_contract_test; + private_create_tx.gas = 200000.into(); + let private_create_tx_signed = private_create_tx.sign(&s0.secret(), None); + let validators = vec![s1.address()]; + let (public_tx, _) = pm0.public_creation_transaction(BlockId::Latest, &private_create_tx_signed, &validators, 0.into()).unwrap(); + let public_tx = public_tx.sign(&s0.secret(), chain_id); + + let public_tx_copy = public_tx.clone(); + push_block_with_transactions(&client0, &[public_tx]); + push_block_with_transactions(&client1, &[public_tx_copy]); + + net.sync(); + + //Create private transaction for modifying state + let mut private_tx = Transaction::default(); + private_tx.action = Action::Call(address.clone()); + private_tx.data = "bc64b76d2a00000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap(); //setX(42) + private_tx.gas = 120000.into(); + private_tx.nonce = 1.into(); + let private_tx = private_tx.sign(&s0.secret(), None); + assert!(pm0.create_private_transaction(private_tx).is_ok()); + + //send private transaction message to validator + net.sync(); + + let validator_handler = net.peer(1).private_tx_handler.clone(); + let received_private_transactions = validator_handler.txs.lock().clone(); + assert_eq!(received_private_transactions.len(), 1); + + //process received private transaction message + let private_transaction = received_private_transactions[0].clone(); + assert!(pm1.import_private_transaction(&private_transaction).is_ok()); + + //send signed response + net.sync(); + + let sender_handler = net.peer(0).private_tx_handler.clone(); + let received_signed_private_transactions = sender_handler.signed_txs.lock().clone(); + assert_eq!(received_signed_private_transactions.len(), 1); + + //process signed response + let signed_private_transaction = received_signed_private_transactions[0].clone(); + assert!(pm0.import_signed_private_transaction(&signed_private_transaction).is_ok()); + let local_transactions = net.peer(0).miner.local_transactions(); + assert_eq!(local_transactions.len(), 1); +} diff --git a/sync/src/tests/rpc.rs b/ethcore/sync/src/tests/rpc.rs similarity index 95% rename from sync/src/tests/rpc.rs rename to ethcore/sync/src/tests/rpc.rs index 5806fbbd8d40114f5c2e1045d2a9eb259daf2acd..99e95959be6a091687728a96ffa08d223ae3bb88 100644 --- a/sync/src/tests/rpc.rs +++ b/ethcore/sync/src/tests/rpc.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/sync/src/tests/snapshot.rs b/ethcore/sync/src/tests/snapshot.rs similarity index 90% rename from sync/src/tests/snapshot.rs rename to ethcore/sync/src/tests/snapshot.rs index 516e3d7e2f0383f5caac09a6af6edce0628273f1..e6636c02f42385deb1ab2d02f2dfd362eddff51d 100644 --- a/sync/src/tests/snapshot.rs +++ b/ethcore/sync/src/tests/snapshot.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -22,9 +22,9 @@ use parking_lot::Mutex; use bytes::Bytes; use ethcore::snapshot::{SnapshotService, ManifestData, RestorationStatus}; use ethcore::header::BlockNumber; -use ethcore::client::{EachBlockWith}; +use ethcore::client::EachBlockWith; use super::helpers::*; -use SyncConfig; +use {SyncConfig, WarpSync}; pub struct TestSnapshotService { manifest: Option, @@ -80,6 +80,10 @@ impl SnapshotService for TestSnapshotService { Some((1, 2)) } + fn completed_chunks(&self) -> Option> { + Some(vec![]) + } + fn chunk(&self, hash: H256) -> Option { self.chunks.get(&hash).cloned() } @@ -99,7 +103,15 @@ impl SnapshotService for TestSnapshotService { } fn begin_restore(&self, manifest: ManifestData) { - *self.restoration_manifest.lock() = Some(manifest); + let mut restoration_manifest = self.restoration_manifest.lock(); + + if let Some(ref c_manifest) = *restoration_manifest { + if c_manifest.state_root == manifest.state_root { + return; + } + } + + *restoration_manifest = Some(manifest); self.state_restoration_chunks.lock().clear(); self.block_restoration_chunks.lock().clear(); } @@ -121,13 +133,17 @@ impl SnapshotService for TestSnapshotService { self.block_restoration_chunks.lock().insert(hash, chunk); } } + + fn shutdown(&self) { + self.abort_restore(); + } } #[test] fn snapshot_sync() { ::env_logger::init().ok(); let mut config = SyncConfig::default(); - config.warp_sync = true; + config.warp_sync = WarpSync::Enabled; let mut net = TestNet::new_with_config(5, config); let snapshot_service = Arc::new(TestSnapshotService::new_with_snapshot(16, H256::new(), 500000)); for i in 0..4 { @@ -138,4 +154,3 @@ fn snapshot_sync() { assert_eq!(net.peer(4).snapshot_service.state_restoration_chunks.lock().len(), net.peer(0).snapshot_service.manifest.as_ref().unwrap().state_hashes.len()); assert_eq!(net.peer(4).snapshot_service.block_restoration_chunks.lock().len(), net.peer(0).snapshot_service.manifest.as_ref().unwrap().block_hashes.len()); } - diff --git a/sync/src/transactions_stats.rs b/ethcore/sync/src/transactions_stats.rs similarity index 98% rename from sync/src/transactions_stats.rs rename to ethcore/sync/src/transactions_stats.rs index 4d33008621a2e35e06b2611051cf6c22cd7e1d6b..c45b1ad8b3cf895385c72ca7bc74e00b41b7fc00 100644 --- a/sync/src/transactions_stats.rs +++ b/ethcore/sync/src/transactions_stats.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/transaction/Cargo.toml b/ethcore/transaction/Cargo.toml index 7b45cae479b5b6dea4fb92fb2d00fef4b51a65b6..79e7282c388d2836adf21a85cca73dbaa8ea6782 100644 --- a/ethcore/transaction/Cargo.toml +++ b/ethcore/transaction/Cargo.toml @@ -12,7 +12,7 @@ heapsize = "0.4" keccak-hash = { path = "../../util/hash" } rlp = { path = "../../util/rlp" } unexpected = { path = "../../util/unexpected" } -ethereum-types = "0.2" +ethereum-types = "0.3" [dev-dependencies] rustc-hex= "1.0" diff --git a/ethcore/transaction/src/error.rs b/ethcore/transaction/src/error.rs index 4578b88acd040f8cc83055e89808ee286b91db70..0efd18ae6b363d883e5a49b983f98a52824035ab 100644 --- a/ethcore/transaction/src/error.rs +++ b/ethcore/transaction/src/error.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -14,10 +14,11 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use std::fmt; +use std::{fmt, error}; use ethereum_types::U256; use ethkey; +use rlp; use unexpected::OutOfBounds; #[derive(Debug, PartialEq, Clone)] @@ -74,6 +75,10 @@ pub enum Error { NotAllowed, /// Signature error InvalidSignature(String), + /// Transaction too big + TooBig, + /// Invalid RLP encoding + InvalidRlp(String), } impl From for Error { @@ -82,6 +87,12 @@ impl From for Error { } } +impl From for Error { + fn from(err: rlp::DecoderError) -> Self { + Error::InvalidRlp(format!("{}", err)) + } +} + impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use self::Error::*; @@ -106,9 +117,16 @@ impl fmt::Display for Error { InvalidChainId => "Transaction of this chain ID is not allowed on this chain.".into(), InvalidSignature(ref err) => format!("Transaction has invalid signature: {}.", err), NotAllowed => "Sender does not have permissions to execute this type of transction".into(), + TooBig => "Transaction too big".into(), + InvalidRlp(ref err) => format!("Transaction has invalid RLP structure: {}.", err), }; f.write_fmt(format_args!("Transaction error ({})", msg)) } } +impl error::Error for Error { + fn description(&self) -> &str { + "Transaction error" + } +} diff --git a/ethcore/transaction/src/lib.rs b/ethcore/transaction/src/lib.rs index 6d6c269f30ca7797f44df606913dae255e41e2ac..829613cf9c79546adf1f61df6800b5cd215a2447 100644 --- a/ethcore/transaction/src/lib.rs +++ b/ethcore/transaction/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -33,14 +33,3 @@ mod transaction; pub use error::Error; pub use transaction::*; - -// TODO [ToDr] Move to miner! - -/// Represents the result of importing transaction. -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum ImportResult { - /// Transaction was imported to current queue. - Current, - /// Transaction was imported to future queue. - Future -} diff --git a/ethcore/transaction/src/transaction.rs b/ethcore/transaction/src/transaction.rs index f206c549a7cd17433bc16a3e9b4452d2d7e9edf1..27b8b346cf25925a6adb8427676a024956cef5fe 100644 --- a/ethcore/transaction/src/transaction.rs +++ b/ethcore/transaction/src/transaction.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -24,8 +24,7 @@ use ethkey::{self, Signature, Secret, Public, recover, public_to_address}; use evm::Schedule; use hash::keccak; use heapsize::HeapSizeOf; -use rlp::{self, RlpStream, UntrustedRlp, DecoderError, Encodable}; -// use rlp::*; +use rlp::{self, RlpStream, Rlp, DecoderError, Encodable}; type Bytes = Vec; type BlockNumber = u64; @@ -51,7 +50,7 @@ impl Default for Action { } impl rlp::Decodable for Action { - fn decode(rlp: &UntrustedRlp) -> Result { + fn decode(rlp: &Rlp) -> Result { if rlp.is_empty() { Ok(Action::Create) } else { @@ -78,6 +77,25 @@ pub enum Condition { Timestamp(u64), } +/// Replay protection logic for v part of transaction's signature +pub mod signature { + /// Adds chain id into v + pub fn add_chain_replay_protection(v: u64, chain_id: Option) -> u64 { + v + if let Some(n) = chain_id { 35 + n * 2 } else { 27 } + } + + /// Returns refined v + /// 0 if `v` would have been 27 under "Electrum" notation, 1 if 28 or 4 if invalid. + pub fn check_replay_protection(v: u64) -> u8 { + match v { + v if v == 27 => 0, + v if v == 28 => 1, + v if v > 36 => ((v - 1) % 2) as u8, + _ => 4 + } + } +} + /// A set of information describing an externally-originating message call /// or contract creation operation. #[derive(Default, Debug, Clone, PartialEq, Eq)] @@ -123,7 +141,7 @@ impl HeapSizeOf for Transaction { impl From for SignedTransaction { fn from(t: ethjson::state::Transaction) -> Self { let to: Option = t.to.into(); - let secret = t.secret.map(|s| Secret::from_slice(&s.0)); + let secret = t.secret.map(|s| Secret::from(s.0)); let tx = Transaction { nonce: t.nonce.into(), gas_price: t.gas_price.into(), @@ -187,7 +205,7 @@ impl Transaction { unsigned: self, r: sig.r().into(), s: sig.s().into(), - v: sig.v() as u64 + if let Some(n) = chain_id { 35 + n * 2 } else { 27 }, + v: signature::add_chain_replay_protection(sig.v() as u64, chain_id), hash: 0.into(), }.compute_hash() } @@ -273,7 +291,7 @@ impl Deref for UnverifiedTransaction { } impl rlp::Decodable for UnverifiedTransaction { - fn decode(d: &UntrustedRlp) -> Result { + fn decode(d: &Rlp) -> Result { if d.item_count()? != 9 { return Err(DecoderError::RlpIncorrectListLen); } @@ -331,8 +349,7 @@ impl UnverifiedTransaction { &self.unsigned } - /// 0 if `v` would have been 27 under "Electrum" notation, 1 if 28 or 4 if invalid. - pub fn standard_v(&self) -> u8 { match self.v { v if v == 27 || v == 28 || v > 36 => ((v - 1) % 2) as u8, _ => 4 } } + pub fn standard_v(&self) -> u8 { signature::check_replay_protection(self.v) } /// The `v` value that appears in the RLP. pub fn original_v(&self) -> u64 { self.v } @@ -360,7 +377,7 @@ impl UnverifiedTransaction { } } - /// Get the hash of this header (keccak of the RLP). + /// Get the hash of this transaction (keccak of the RLP). pub fn hash(&self) -> H256 { self.hash } @@ -392,6 +409,10 @@ impl UnverifiedTransaction { if check_low_s && !(allow_empty_signature && self.is_unsigned()) { self.check_low_s()?; } + // Disallow unsigned transactions in case EIP-86 is disabled. + if !allow_empty_signature && self.is_unsigned() { + return Err(ethkey::Error::InvalidSignature.into()); + } // EIP-86: Transactions of this form MUST have gasprice = 0, nonce = 0, value = 0, and do NOT increment the nonce of account 0. if allow_empty_signature && self.is_unsigned() && !(self.gas_price.is_zero() && self.value.is_zero() && self.nonce.is_zero()) { return Err(ethkey::Error::InvalidSignature.into()) @@ -559,7 +580,8 @@ mod tests { #[test] fn sender_test() { - let t: UnverifiedTransaction = rlp::decode(&::rustc_hex::FromHex::from_hex("f85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804").unwrap()); + let bytes = ::rustc_hex::FromHex::from_hex("f85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804").unwrap(); + let t: UnverifiedTransaction = rlp::decode(&bytes).expect("decoding UnverifiedTransaction failed"); assert_eq!(t.data, b""); assert_eq!(t.gas, U256::from(0x5208u64)); assert_eq!(t.gas_price, U256::from(0x01u64)); @@ -628,7 +650,7 @@ mod tests { use rustc_hex::FromHex; let test_vector = |tx_data: &str, address: &'static str| { - let signed = rlp::decode(&FromHex::from_hex(tx_data).unwrap()); + let signed = rlp::decode(&FromHex::from_hex(tx_data).unwrap()).expect("decoding tx data failed"); let signed = SignedTransaction::new(signed).unwrap(); assert_eq!(signed.sender(), address.into()); println!("chainid: {:?}", signed.chain_id()); diff --git a/ethcore/types/Cargo.toml b/ethcore/types/Cargo.toml index a14929f47edbc3332c4edfbb4fe6fc3f3568fec3..92cc74551d38bc321b8c81c08d548a7e0569fe90 100644 --- a/ethcore/types/Cargo.toml +++ b/ethcore/types/Cargo.toml @@ -8,7 +8,7 @@ authors = ["Parity Technologies "] rlp = { path = "../../util/rlp" } rlp_derive = { path = "../../util/rlp_derive" } ethcore-bytes = { path = "../../util/bytes" } -ethereum-types = "0.2" +ethereum-types = "0.3" ethjson = { path = "../../json" } keccak-hash = { path = "../../util/hash" } heapsize = "0.4" diff --git a/ethcore/types/src/account_diff.rs b/ethcore/types/src/account_diff.rs index c3edb1fb1e3d18186346306dcfeacf5b207b4e24..521ed8ab1f87c06a3226de18bbdb23b8a12b7960 100644 --- a/ethcore/types/src/account_diff.rs +++ b/ethcore/types/src/account_diff.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -138,4 +138,3 @@ impl fmt::Display for AccountDiff { Ok(()) } } - diff --git a/ethcore/types/src/ancestry_action.rs b/ethcore/types/src/ancestry_action.rs new file mode 100644 index 0000000000000000000000000000000000000000..d9915cfee422cec39236632c30195874dc30b266 --- /dev/null +++ b/ethcore/types/src/ancestry_action.rs @@ -0,0 +1,27 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Actions on ancestry blocks when working on a new block. + +use ethereum_types::H256; + +#[derive(Debug, PartialEq, Eq, Clone)] +/// Actions on a live block's parent block. Only committed when the live block is committed. Those actions here must +/// respect the normal blockchain reorganization rules. +pub enum AncestryAction { + /// Mark an ancestry block as finalized. + MarkFinalized(H256), +} diff --git a/ethcore/types/src/basic_account.rs b/ethcore/types/src/basic_account.rs index 79e75dfc0144a72176fd1211df57111524598e90..94157977bc7d30a5be26bb7413c1f49c1b8a2fd1 100644 --- a/ethcore/types/src/basic_account.rs +++ b/ethcore/types/src/basic_account.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/types/src/block_status.rs b/ethcore/types/src/block_status.rs index d330b9ed1b01522efb2b2b5d0e057e229423cc74..5455f1d40f216550eb492bd5eec9505928aa6a83 100644 --- a/ethcore/types/src/block_status.rs +++ b/ethcore/types/src/block_status.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/types/src/blockchain_info.rs b/ethcore/types/src/blockchain_info.rs index 836ee7618bd7f0ac5b18ec13ee1da34c888190e9..ddd91623d123c6e7a6398b4ec5bce38e1627a33f 100644 --- a/ethcore/types/src/blockchain_info.rs +++ b/ethcore/types/src/blockchain_info.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/types/src/call_analytics.rs b/ethcore/types/src/call_analytics.rs index b0520a0d3ff67b3724570efa416f969beed3cd87..ae53e6911e136a969bc11f473d881a7fbc96fc18 100644 --- a/ethcore/types/src/call_analytics.rs +++ b/ethcore/types/src/call_analytics.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/types/src/filter.rs b/ethcore/types/src/filter.rs index 0a37482b94e66aaf23b8ed3e4375e30ffa5a7aab..c32551473d133a4b5d8a4be90dd820aa56c277c3 100644 --- a/ethcore/types/src/filter.rs +++ b/ethcore/types/src/filter.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/types/src/ids.rs b/ethcore/types/src/ids.rs index e304698a4ca83c01506bc43b1ca1d390340a6f6d..d1457832c010fa1141ae6afd23a19e050cb46538 100644 --- a/ethcore/types/src/ids.rs +++ b/ethcore/types/src/ids.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/types/src/lib.rs b/ethcore/types/src/lib.rs index 83a1cc65579e4d8a46b11e42c2d4286011b31bf6..5ac8ff12add017d755ab27f9ef770c91eedf17f6 100644 --- a/ethcore/types/src/lib.rs +++ b/ethcore/types/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -36,7 +36,6 @@ pub mod call_analytics; pub mod filter; pub mod ids; pub mod log_entry; -pub mod mode; pub mod pruning_info; pub mod receipt; pub mod restoration_status; @@ -46,6 +45,7 @@ pub mod state_diff; pub mod trace_filter; pub mod tree_route; pub mod verification_queue_info; +pub mod ancestry_action; /// Type for block number. pub type BlockNumber = u64; diff --git a/ethcore/types/src/log_entry.rs b/ethcore/types/src/log_entry.rs index 951a7389f2901b28179c5e85f1ea934f3eb83ec4..0b7455df496e0f3b48f3a79b6159180a42ff70f5 100644 --- a/ethcore/types/src/log_entry.rs +++ b/ethcore/types/src/log_entry.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/types/src/pruning_info.rs b/ethcore/types/src/pruning_info.rs index 8a47fdd8b902ee72703547e21349f96ceaf61fd3..fcf4a774a20317f2f39f19493b6335ccd2ad36f4 100644 --- a/ethcore/types/src/pruning_info.rs +++ b/ethcore/types/src/pruning_info.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/types/src/receipt.rs b/ethcore/types/src/receipt.rs index 29b2e909b9a57ef548a0605c27e21765936d3aae..81233d212a204913ddf657f202a01a8abfe9b03f 100644 --- a/ethcore/types/src/receipt.rs +++ b/ethcore/types/src/receipt.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -16,9 +16,9 @@ //! Receipt -use ethereum_types::{H256, U256, Address, Bloom}; +use ethereum_types::{H160, H256, U256, Address, Bloom}; use heapsize::HeapSizeOf; -use rlp::*; +use rlp::{Rlp, RlpStream, Encodable, Decodable, DecoderError}; use {BlockNumber}; use log_entry::{LogEntry, LocalizedLogEntry}; @@ -81,7 +81,7 @@ impl Encodable for Receipt { } impl Decodable for Receipt { - fn decode(rlp: &UntrustedRlp) -> Result { + fn decode(rlp: &Rlp) -> Result { if rlp.item_count()? == 3 { Ok(Receipt { outcome: TransactionOutcome::Unknown, @@ -157,6 +157,10 @@ pub struct LocalizedReceipt { pub log_bloom: Bloom, /// Transaction outcome. pub outcome: TransactionOutcome, + /// Receiver address + pub to: Option, + /// Sender + pub from: H160 } #[cfg(test)] @@ -193,7 +197,7 @@ mod tests { ); let encoded = ::rlp::encode(&r); assert_eq!(&encoded[..], &expected[..]); - let decoded: Receipt = ::rlp::decode(&encoded); + let decoded: Receipt = ::rlp::decode(&encoded).expect("decoding receipt failed"); assert_eq!(decoded, r); } @@ -211,7 +215,7 @@ mod tests { ); let encoded = ::rlp::encode(&r); assert_eq!(&encoded[..], &expected[..]); - let decoded: Receipt = ::rlp::decode(&encoded); + let decoded: Receipt = ::rlp::decode(&encoded).expect("decoding receipt failed"); assert_eq!(decoded, r); } } diff --git a/ethcore/types/src/restoration_status.rs b/ethcore/types/src/restoration_status.rs index 0cc7fccc08e729961c0974ff5897266726eabd9c..ec15bf4809fefa95288dc07620b7c1e7a8b8a453 100644 --- a/ethcore/types/src/restoration_status.rs +++ b/ethcore/types/src/restoration_status.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -21,6 +21,11 @@ pub enum RestorationStatus { /// No restoration. Inactive, + /// Restoration is initalizing + Initializing { + /// Number of chunks done/imported + chunks_done: u32, + }, /// Ongoing restoration. Ongoing { /// Total number of state chunks. @@ -35,4 +40,3 @@ pub enum RestorationStatus { /// Failed restoration. Failed, } - diff --git a/ethcore/types/src/security_level.rs b/ethcore/types/src/security_level.rs index ea39dc32807f6c01f7173300a4c673f06a1de835..5917584704673e84b9ff6a1dbe57e7c4fc7c944d 100644 --- a/ethcore/types/src/security_level.rs +++ b/ethcore/types/src/security_level.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/types/src/snapshot_manifest.rs b/ethcore/types/src/snapshot_manifest.rs index 72f83afeb2829e75d77e37e0ba809bbbcd5a76d7..40ff4c532f70528c1c5fc4be4100d3e6d2e35801 100644 --- a/ethcore/types/src/snapshot_manifest.rs +++ b/ethcore/types/src/snapshot_manifest.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -17,7 +17,7 @@ //! Snapshot manifest type definition use ethereum_types::H256; -use rlp::*; +use rlp::{Rlp, RlpStream, DecoderError}; use bytes::Bytes; /// Manifest data. @@ -53,7 +53,7 @@ impl ManifestData { /// Try to restore manifest data from raw bytes, interpreted as RLP. pub fn from_rlp(raw: &[u8]) -> Result { - let decoder = UntrustedRlp::new(raw); + let decoder = Rlp::new(raw); let (start, version) = if decoder.item_count()? == 5 { (0, 1) } else { @@ -76,4 +76,3 @@ impl ManifestData { }) } } - diff --git a/ethcore/types/src/state_diff.rs b/ethcore/types/src/state_diff.rs index dd976eb36c6938a326f1bc99f0e82f56cf07ee26..4cc85fff93f23c6e391b3d0e477ff67d2a2f2894 100644 --- a/ethcore/types/src/state_diff.rs +++ b/ethcore/types/src/state_diff.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/types/src/trace_filter.rs b/ethcore/types/src/trace_filter.rs index 2afa752ccbb44d054541ea1b20aa1229d944872e..69a37870277efefb406f9da698585680befd6264 100644 --- a/ethcore/types/src/trace_filter.rs +++ b/ethcore/types/src/trace_filter.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/types/src/tree_route.rs b/ethcore/types/src/tree_route.rs index b3fe431ab9973bc0279fb8aaf2deeda217ac7f18..9c84052be35ddfba4ae5f806fd3c2dff8984509b 100644 --- a/ethcore/types/src/tree_route.rs +++ b/ethcore/types/src/tree_route.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -27,4 +27,6 @@ pub struct TreeRoute { pub ancestor: H256, /// An index where best common ancestor would be. pub index: usize, + /// Whether it has finalized blocks from `from` (inclusive) to `ancestor` (exclusive). + pub is_from_route_finalized: bool, } diff --git a/ethcore/types/src/verification_queue_info.rs b/ethcore/types/src/verification_queue_info.rs index db818590aff780ffdfec119ab2bf88df4fa81bb5..bc280b15bfea50c24a71a813150872f3c393dca1 100644 --- a/ethcore/types/src/verification_queue_info.rs +++ b/ethcore/types/src/verification_queue_info.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/vm/Cargo.toml b/ethcore/vm/Cargo.toml index 41ff98d0a056dee2c214dc7f0937b20fe3a8ef94..c5d31f58e61578de7ce8d4b677722fede41f8022 100644 --- a/ethcore/vm/Cargo.toml +++ b/ethcore/vm/Cargo.toml @@ -6,7 +6,7 @@ authors = ["Parity Technologies "] [dependencies] byteorder = "1.0" ethcore-bytes = { path = "../../util/bytes" } -ethereum-types = "0.2" +ethereum-types = "0.3" patricia-trie = { path = "../../util/patricia_trie" } log = "0.3" common-types = { path = "../types" } diff --git a/ethcore/vm/src/action_params.rs b/ethcore/vm/src/action_params.rs index 9e9a35528c89e3247ed07683a86c438bb3a3e45d..481f6373104935e516d5e6ba1dbe39a9ee04d340 100644 --- a/ethcore/vm/src/action_params.rs +++ b/ethcore/vm/src/action_params.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/vm/src/call_type.rs b/ethcore/vm/src/call_type.rs index 08a004053f0841e813b216bb592c78610390f152..0e58d76bbd269ca02aeccd2495db6f45993c79b9 100644 --- a/ethcore/vm/src/call_type.rs +++ b/ethcore/vm/src/call_type.rs @@ -1,6 +1,22 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + //! EVM call types. -use rlp::{Encodable, Decodable, DecoderError, RlpStream, UntrustedRlp}; +use rlp::{Encodable, Decodable, DecoderError, RlpStream, Rlp}; /// The type of the call-like instruction. #[derive(Debug, PartialEq, Clone)] @@ -31,7 +47,7 @@ impl Encodable for CallType { } impl Decodable for CallType { - fn decode(rlp: &UntrustedRlp) -> Result { + fn decode(rlp: &Rlp) -> Result { rlp.as_val().and_then(|v| Ok(match v { 0u32 => CallType::None, 1 => CallType::Call, @@ -64,7 +80,7 @@ mod tests { fn should_encode_and_decode_call_type() { let original = CallType::Call; let encoded = encode(&original); - let decoded = decode(&encoded); + let decoded = decode(&encoded).expect("failure decoding CallType"); assert_eq!(original, decoded); } } diff --git a/ethcore/vm/src/env_info.rs b/ethcore/vm/src/env_info.rs index 71bb48eeb721383f127866717f6affb093db59d2..bb1c9ecd91f05d9e4e4890d6ec8f3c875f9990f2 100644 --- a/ethcore/vm/src/env_info.rs +++ b/ethcore/vm/src/env_info.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/vm/src/error.rs b/ethcore/vm/src/error.rs index fe8d7054cfe1ff8d59490fee0216f1a18dbcc7cf..ad23e3e020e5120548e6ca972109d25bea1ea521 100644 --- a/ethcore/vm/src/error.rs +++ b/ethcore/vm/src/error.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -71,7 +71,6 @@ pub enum Error { Reverted, } - impl From> for Error { fn from(err: Box) -> Self { Error::Internal(format!("Internal error: {}", err)) diff --git a/ethcore/vm/src/ext.rs b/ethcore/vm/src/ext.rs index 98661e47e2f8d9c6f82de8ad5f254b7b980e3e1c..166e8712aaea440fbc013a376e99dffe5192b220 100644 --- a/ethcore/vm/src/ext.rs +++ b/ethcore/vm/src/ext.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/vm/src/lib.rs b/ethcore/vm/src/lib.rs index 67fc59bab5ec02338738ea39c0bdbd59af10f443..0dc1b7995491fd538ca526a7b8cbc868c66f81c0 100644 --- a/ethcore/vm/src/lib.rs +++ b/ethcore/vm/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/vm/src/return_data.rs b/ethcore/vm/src/return_data.rs index 067a26e35e33a5eb3aa0e59a3f2eef3f1b498615..24191ec55f61240a7db1a57911090f2381201374 100644 --- a/ethcore/vm/src/return_data.rs +++ b/ethcore/vm/src/return_data.rs @@ -1,3 +1,5 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. // Parity is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by diff --git a/ethcore/vm/src/schedule.rs b/ethcore/vm/src/schedule.rs index 6cfabed0df3c4f76acb500648f551f115aa1cbbd..960821e72c7eff218e875833d58a040dde38c655 100644 --- a/ethcore/vm/src/schedule.rs +++ b/ethcore/vm/src/schedule.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -109,6 +109,8 @@ pub struct Schedule { pub have_static_call: bool, /// RETURNDATA and RETURNDATASIZE opcodes enabled. pub have_return_data: bool, + /// SHL, SHR, SAR opcodes enabled. + pub have_bitwise_shifting: bool, /// Kill basic accounts below this balance if touched. pub kill_dust: CleanDustMode, /// Enable EIP-86 rules @@ -194,6 +196,7 @@ impl Schedule { have_create2: false, have_revert: false, have_return_data: false, + have_bitwise_shifting: false, stack_limit: 1024, max_depth: 1024, tier_step_gas: [0, 2, 3, 5, 8, 10, 20, 0], @@ -250,6 +253,13 @@ impl Schedule { schedule } + /// Schedule for the Constantinople fork of the Ethereum main net. + pub fn new_constantinople() -> Schedule { + let mut schedule = Self::new_byzantium(); + schedule.have_bitwise_shifting = true; + schedule + } + fn new(efcd: bool, hdc: bool, tcg: usize) -> Schedule { Schedule { exceptional_failed_code_deposit: efcd, @@ -257,6 +267,7 @@ impl Schedule { have_create2: false, have_revert: false, have_return_data: false, + have_bitwise_shifting: false, stack_limit: 1024, max_depth: 1024, tier_step_gas: [0, 2, 3, 5, 8, 10, 20, 0], @@ -328,4 +339,3 @@ fn schedule_evm_assumptions() { assert_eq!(s1.quad_coeff_div, 512); assert_eq!(s2.quad_coeff_div, 512); } - diff --git a/ethcore/vm/src/tests.rs b/ethcore/vm/src/tests.rs index ce47121458f998a5efb58096c20d1b2b74655a6a..9a17e0d3dc46ea7547461a93d9e77c233ff1e0a2 100644 --- a/ethcore/vm/src/tests.rs +++ b/ethcore/vm/src/tests.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -88,6 +88,13 @@ impl FakeExt { ext } + /// New fake externalities with constantinople schedule rules + pub fn new_constantinople() -> Self { + let mut ext = FakeExt::default(); + ext.schedule = Schedule::new_constantinople(); + ext + } + /// Alter fake externalities to allow wasm pub fn with_wasm(mut self) -> Self { self.schedule.wasm = Some(Default::default()); diff --git a/ethcore/wasm/Cargo.toml b/ethcore/wasm/Cargo.toml index fc820a67d4c8c623b6ad2c56b2da1119784b0836..a0362955d1db998943b1284a601f045d6a5d2a16 100644 --- a/ethcore/wasm/Cargo.toml +++ b/ethcore/wasm/Cargo.toml @@ -5,11 +5,11 @@ authors = ["Parity Technologies "] [dependencies] byteorder = "1.0" -ethereum-types = "0.2" +ethereum-types = "0.3" log = "0.3" parity-wasm = "0.27" libc = "0.2" pwasm-utils = "0.1" vm = { path = "../vm" } ethcore-logger = { path = "../../logger" } -wasmi = { version = "0.1", features = ["opt-in-32bit"] } +wasmi = { version = "0.2" } diff --git a/ethcore/wasm/run/Cargo.toml b/ethcore/wasm/run/Cargo.toml index 91cbfb996c391727878265cef79179c6d27d2c01..514849f2afae92257655b9a771d89c044cf4b394 100644 --- a/ethcore/wasm/run/Cargo.toml +++ b/ethcore/wasm/run/Cargo.toml @@ -7,7 +7,7 @@ authors = ["Parity Technologies "] serde = "1" serde_json = "1" serde_derive = "1" -ethereum-types = "0.2" +ethereum-types = "0.3" ethjson = { path = "../../../json" } vm = { path = "../../vm" } wasm = { path = "../" } diff --git a/ethcore/wasm/run/src/fixture.rs b/ethcore/wasm/run/src/fixture.rs index ba2da0670677a1d80473688ad57d0752684c1811..9fc1ca6fefb1b5c9b5b41d4307b22f7088e82f0d 100644 --- a/ethcore/wasm/run/src/fixture.rs +++ b/ethcore/wasm/run/src/fixture.rs @@ -1,3 +1,19 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + use std::borrow::Cow; use ethjson::uint::Uint; use ethjson::hash::{Address, H256}; @@ -67,4 +83,4 @@ pub enum Assert { HasStorage(StorageAssert), UsedGas(u64), Return(Bytes), -} \ No newline at end of file +} diff --git a/ethcore/wasm/run/src/main.rs b/ethcore/wasm/run/src/main.rs index ab8ac631df9136e8263fce5d5e3e8d9dba7a788d..d2a3a0ff5061a89adae744fa3a8e3f9906d96ee4 100644 --- a/ethcore/wasm/run/src/main.rs +++ b/ethcore/wasm/run/src/main.rs @@ -1,3 +1,19 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + extern crate serde; extern crate serde_json; #[macro_use] extern crate serde_derive; diff --git a/ethcore/wasm/run/src/runner.rs b/ethcore/wasm/run/src/runner.rs index a30efc6f086c8d8c33a057b769bf466acdb1def8..3e24ced5db2da24885371f9652b349ee437e1e1d 100644 --- a/ethcore/wasm/run/src/runner.rs +++ b/ethcore/wasm/run/src/runner.rs @@ -1,3 +1,19 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + use fixture::{Fixture, Assert, CallLocator, Source}; use wasm::WasmInterpreter; use vm::{self, Vm, GasLeft, ActionParams, ActionValue, ParamsType}; @@ -79,17 +95,17 @@ impl fmt::Display for Fail { write!( f, "Storage key {} value mismatch, expected {}, got: {}", - key.as_ref().to_vec().to_hex(), - expected.as_ref().to_vec().to_hex(), - actual.as_ref().to_vec().to_hex(), + key.to_vec().to_hex(), + expected.to_vec().to_hex(), + actual.to_vec().to_hex(), ), StorageMismatch { ref key, ref expected, actual: None} => write!( f, "No expected storage value for key {} found, expected {}", - key.as_ref().to_vec().to_hex(), - expected.as_ref().to_vec().to_hex(), + key.to_vec().to_hex(), + expected.to_vec().to_hex(), ), Nonconformity(SpecNonconformity::Address) => diff --git a/ethcore/wasm/src/env.rs b/ethcore/wasm/src/env.rs index 7ffaaf98abd2f04797e9fcd0c30abdc51d5085cb..9bcbee63fb0eedf2fb8f9c0a6b0308839d9c2f42 100644 --- a/ethcore/wasm/src/env.rs +++ b/ethcore/wasm/src/env.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -295,4 +295,4 @@ impl wasmi::ModuleImportResolver for ImportResolver { Err(Error::Instantiation("Memory imported under unknown name".to_owned())) } } -} \ No newline at end of file +} diff --git a/ethcore/wasm/src/lib.rs b/ethcore/wasm/src/lib.rs index 5605a7ea185683c44d0d1e542b7624748e98d20d..f1290318e0f2b7a8b7988002dd11d1e259c3a178 100644 --- a/ethcore/wasm/src/lib.rs +++ b/ethcore/wasm/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/wasm/src/panic_payload.rs b/ethcore/wasm/src/panic_payload.rs index dc95f53fbfc9bca70fbb3b65c5885c120e795489..36aa6c5f585be93f03957dbe048670a8e618ce6a 100644 --- a/ethcore/wasm/src/panic_payload.rs +++ b/ethcore/wasm/src/panic_payload.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcore/wasm/src/parser.rs b/ethcore/wasm/src/parser.rs index 62cd66cb98c8e50fa13f665dbc24496d11d13eb7..1efb89e1bdd4cf843b71ecbea32f83709d6051c1 100644 --- a/ethcore/wasm/src/parser.rs +++ b/ethcore/wasm/src/parser.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -95,4 +95,4 @@ pub fn payload<'a>(params: &'a vm::ActionParams, wasm_costs: &vm::WasmCosts) }; Ok((contract_module, data)) -} \ No newline at end of file +} diff --git a/ethcore/wasm/src/runtime.rs b/ethcore/wasm/src/runtime.rs index 71fafd671e368d05e7e7706be72299609f93b2cb..d99ab8645a1f58b54d16afd46f4f269910dcb49a 100644 --- a/ethcore/wasm/src/runtime.rs +++ b/ethcore/wasm/src/runtime.rs @@ -1,3 +1,19 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + use ethereum_types::{U256, H256, Address}; use vm::{self, CallType}; use wasmi::{self, MemoryRef, RuntimeArgs, RuntimeValue, Error as InterpreterError, Trap, TrapKind}; diff --git a/ethcore/wasm/src/tests.rs b/ethcore/wasm/src/tests.rs index 2b71a1768eb395befbb6c2d1da3b55684a1c19de..b32ca75ae965f47124b0cedc0e7d35e801faa124 100644 --- a/ethcore/wasm/src/tests.rs +++ b/ethcore/wasm/src/tests.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethcrypto/Cargo.toml b/ethcrypto/Cargo.toml deleted file mode 100644 index dfa351023cd1dce00acb8d57937c20028247d438..0000000000000000000000000000000000000000 --- a/ethcrypto/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "ethcrypto" -version = "0.1.0" -authors = ["Parity Technologies "] - -[dependencies] -rust-crypto = "0.2.36" -tiny-keccak = "1.3" -eth-secp256k1 = { git = "https://github.com/paritytech/rust-secp256k1", optional = true } -ethkey = { path = "../ethkey", optional = true } -ethereum-types = "0.2" -subtle = "0.5" - -[features] -default = ["secp256k1"] -secp256k1 = ["eth-secp256k1", "ethkey"] diff --git a/ethcrypto/src/lib.rs b/ethcrypto/src/lib.rs deleted file mode 100644 index caa4cf77c4d80bfde6824fe5e1c8c4de00ce9b11..0000000000000000000000000000000000000000 --- a/ethcrypto/src/lib.rs +++ /dev/null @@ -1,344 +0,0 @@ -// Copyright 2015-2017 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 . - -//! Crypto utils used ethstore and network. - -extern crate crypto as rcrypto; -extern crate ethereum_types; -extern crate subtle; -extern crate tiny_keccak; - -#[cfg(feature = "secp256k1")] -extern crate secp256k1; -#[cfg(feature = "secp256k1")] -extern crate ethkey; - -use std::fmt; -use tiny_keccak::Keccak; -use rcrypto::pbkdf2::pbkdf2; -use rcrypto::scrypt::{scrypt, ScryptParams}; -use rcrypto::sha2::Sha256; -use rcrypto::hmac::Hmac; - -#[cfg(feature = "secp256k1")] -use secp256k1::Error as SecpError; - -pub const KEY_LENGTH: usize = 32; -pub const KEY_ITERATIONS: usize = 10240; -pub const KEY_LENGTH_AES: usize = KEY_LENGTH / 2; - -/// Default authenticated data to use (in RPC). -pub const DEFAULT_MAC: [u8; 2] = [0, 0]; - -#[derive(PartialEq, Debug)] -pub enum ScryptError { - // log(N) < r / 16 - InvalidN, - // p <= (2^31-1 * 32)/(128 * r) - InvalidP, -} - -impl fmt::Display for ScryptError { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - let s = match *self { - ScryptError::InvalidN => "Invalid N argument of the scrypt encryption" , - ScryptError::InvalidP => "Invalid p argument of the scrypt encryption", - }; - - write!(f, "{}", s) - } -} - -#[derive(PartialEq, Debug)] -pub enum Error { - #[cfg(feature = "secp256k1")] - Secp(SecpError), - Scrypt(ScryptError), - InvalidMessage, -} - -impl From for Error { - fn from(err: ScryptError) -> Self { - Error::Scrypt(err) - } -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - let s = match *self { - #[cfg(feature = "secp256k1")] - Error::Secp(ref err) => err.to_string(), - Error::Scrypt(ref err) => err.to_string(), - Error::InvalidMessage => "Invalid message".into(), - }; - - write!(f, "{}", s) - } -} - -impl Into for Error { - fn into(self) -> String { - format!("{}", self) - } -} - -#[cfg(feature = "secp256k1")] -impl From for Error { - fn from(e: SecpError) -> Self { - Error::Secp(e) - } -} - -pub trait Keccak256 { - fn keccak256(&self) -> T where T: Sized; -} - -impl Keccak256<[u8; 32]> for T where T: AsRef<[u8]> { - fn keccak256(&self) -> [u8; 32] { - let mut keccak = Keccak::new_keccak256(); - let mut result = [0u8; 32]; - keccak.update(self.as_ref()); - keccak.finalize(&mut result); - result - } -} - -pub fn derive_key_iterations(password: &str, salt: &[u8; 32], c: u32) -> (Vec, Vec) { - let mut h_mac = Hmac::new(Sha256::new(), password.as_bytes()); - let mut derived_key = vec![0u8; KEY_LENGTH]; - pbkdf2(&mut h_mac, salt, c, &mut derived_key); - let derived_right_bits = &derived_key[0..KEY_LENGTH_AES]; - let derived_left_bits = &derived_key[KEY_LENGTH_AES..KEY_LENGTH]; - (derived_right_bits.to_vec(), derived_left_bits.to_vec()) -} - -pub fn derive_key_scrypt(password: &str, salt: &[u8; 32], n: u32, p: u32, r: u32) -> Result<(Vec, Vec), Error> { - // sanity checks - let log_n = (32 - n.leading_zeros() - 1) as u8; - if log_n as u32 >= r * 16 { - return Err(Error::Scrypt(ScryptError::InvalidN)); - } - - if p as u64 > ((u32::max_value() as u64 - 1) * 32)/(128 * (r as u64)) { - return Err(Error::Scrypt(ScryptError::InvalidP)); - } - - let mut derived_key = vec![0u8; KEY_LENGTH]; - let scrypt_params = ScryptParams::new(log_n, r, p); - scrypt(password.as_bytes(), salt, &scrypt_params, &mut derived_key); - let derived_right_bits = &derived_key[0..KEY_LENGTH_AES]; - let derived_left_bits = &derived_key[KEY_LENGTH_AES..KEY_LENGTH]; - Ok((derived_right_bits.to_vec(), derived_left_bits.to_vec())) -} - -pub fn derive_mac(derived_left_bits: &[u8], cipher_text: &[u8]) -> Vec { - let mut mac = vec![0u8; KEY_LENGTH_AES + cipher_text.len()]; - mac[0..KEY_LENGTH_AES].copy_from_slice(derived_left_bits); - mac[KEY_LENGTH_AES..cipher_text.len() + KEY_LENGTH_AES].copy_from_slice(cipher_text); - mac -} - -/// AES encryption -pub mod aes { - use rcrypto::blockmodes::{CtrMode, CbcDecryptor, PkcsPadding}; - use rcrypto::aessafe::{AesSafe128Encryptor, AesSafe128Decryptor}; - use rcrypto::symmetriccipher::{Encryptor, Decryptor, SymmetricCipherError}; - use rcrypto::buffer::{RefReadBuffer, RefWriteBuffer, WriteBuffer}; - - /// Encrypt a message (CTR mode) - pub fn encrypt(k: &[u8], iv: &[u8], plain: &[u8], dest: &mut [u8]) { - let mut encryptor = CtrMode::new(AesSafe128Encryptor::new(k), iv.to_vec()); - encryptor.encrypt(&mut RefReadBuffer::new(plain), &mut RefWriteBuffer::new(dest), true).expect("Invalid length or padding"); - } - - /// Decrypt a message (CTR mode) - pub fn decrypt(k: &[u8], iv: &[u8], encrypted: &[u8], dest: &mut [u8]) { - let mut encryptor = CtrMode::new(AesSafe128Encryptor::new(k), iv.to_vec()); - encryptor.decrypt(&mut RefReadBuffer::new(encrypted), &mut RefWriteBuffer::new(dest), true).expect("Invalid length or padding"); - } - - - /// Decrypt a message using cbc mode - pub fn decrypt_cbc(k: &[u8], iv: &[u8], encrypted: &[u8], dest: &mut [u8]) -> Result { - let mut encryptor = CbcDecryptor::new(AesSafe128Decryptor::new(k), PkcsPadding, iv.to_vec()); - let len = dest.len(); - let mut buffer = RefWriteBuffer::new(dest); - encryptor.decrypt(&mut RefReadBuffer::new(encrypted), &mut buffer, true)?; - Ok(len - buffer.remaining()) - } -} - -/// ECDH functions -#[cfg(feature = "secp256k1")] -pub mod ecdh { - use secp256k1::{ecdh, key, Error as SecpError}; - use ethkey::{Secret, Public, SECP256K1}; - use Error; - - /// Agree on a shared secret - pub fn agree(secret: &Secret, public: &Public) -> Result { - let context = &SECP256K1; - let pdata = { - let mut temp = [4u8; 65]; - (&mut temp[1..65]).copy_from_slice(&public[0..64]); - temp - }; - - let publ = key::PublicKey::from_slice(context, &pdata)?; - let sec = key::SecretKey::from_slice(context, &secret)?; - let shared = ecdh::SharedSecret::new_raw(context, &publ, &sec); - - Secret::from_unsafe_slice(&shared[0..32]) - .map_err(|_| Error::Secp(SecpError::InvalidSecretKey)) - } -} - -/// ECIES function -#[cfg(feature = "secp256k1")] -pub mod ecies { - use rcrypto::digest::Digest; - use rcrypto::sha2::Sha256; - use rcrypto::hmac::Hmac; - use rcrypto::mac::Mac; - use ethereum_types::H128; - use ethkey::{Random, Generator, Public, Secret}; - use {Error, ecdh, aes}; - - /// Encrypt a message with a public key, writing an HMAC covering both - /// the plaintext and authenticated data. - /// - /// Authenticated data may be empty. - pub fn encrypt(public: &Public, auth_data: &[u8], plain: &[u8]) -> Result, Error> { - let r = Random.generate() - .expect("context known to have key-generation capabilities; qed"); - - let z = ecdh::agree(r.secret(), public)?; - let mut key = [0u8; 32]; - let mut mkey = [0u8; 32]; - kdf(&z, &[0u8; 0], &mut key); - let mut hasher = Sha256::new(); - let mkey_material = &key[16..32]; - hasher.input(mkey_material); - hasher.result(&mut mkey); - let ekey = &key[0..16]; - - let mut msg = vec![0u8; 1 + 64 + 16 + plain.len() + 32]; - msg[0] = 0x04u8; - { - let msgd = &mut msg[1..]; - msgd[0..64].copy_from_slice(r.public()); - let iv = H128::random(); - msgd[64..80].copy_from_slice(&iv); - { - let cipher = &mut msgd[(64 + 16)..(64 + 16 + plain.len())]; - aes::encrypt(ekey, &iv, plain, cipher); - } - let mut hmac = Hmac::new(Sha256::new(), &mkey); - { - let cipher_iv = &msgd[64..(64 + 16 + plain.len())]; - hmac.input(cipher_iv); - } - hmac.input(auth_data); - hmac.raw_result(&mut msgd[(64 + 16 + plain.len())..]); - } - Ok(msg) - } - - /// Decrypt a message with a secret key, checking HMAC for ciphertext - /// and authenticated data validity. - pub fn decrypt(secret: &Secret, auth_data: &[u8], encrypted: &[u8]) -> Result, Error> { - let meta_len = 1 + 64 + 16 + 32; - if encrypted.len() < meta_len || encrypted[0] < 2 || encrypted[0] > 4 { - return Err(Error::InvalidMessage); //invalid message: publickey - } - - let e = &encrypted[1..]; - let p = Public::from_slice(&e[0..64]); - let z = ecdh::agree(secret, &p)?; - let mut key = [0u8; 32]; - kdf(&z, &[0u8; 0], &mut key); - let ekey = &key[0..16]; - let mkey_material = &key[16..32]; - let mut hasher = Sha256::new(); - let mut mkey = [0u8; 32]; - hasher.input(mkey_material); - hasher.result(&mut mkey); - - let clen = encrypted.len() - meta_len; - let cipher_with_iv = &e[64..(64+16+clen)]; - let cipher_iv = &cipher_with_iv[0..16]; - let cipher_no_iv = &cipher_with_iv[16..]; - let msg_mac = &e[(64+16+clen)..]; - - // Verify tag - let mut hmac = Hmac::new(Sha256::new(), &mkey); - hmac.input(cipher_with_iv); - hmac.input(auth_data); - let mut mac = [0u8; 32]; - hmac.raw_result(&mut mac); - - // constant time compare to avoid timing attack. - if ::subtle::slices_equal(&mac[..], msg_mac) != 1 { - return Err(Error::InvalidMessage); - } - - let mut msg = vec![0u8; clen]; - aes::decrypt(ekey, cipher_iv, cipher_no_iv, &mut msg[..]); - Ok(msg) - } - - fn kdf(secret: &Secret, s1: &[u8], dest: &mut [u8]) { - let mut hasher = Sha256::new(); - // SEC/ISO/Shoup specify counter size SHOULD be equivalent - // to size of hash output, however, it also notes that - // the 4 bytes is okay. NIST specifies 4 bytes. - let mut ctr = 1u32; - let mut written = 0usize; - while written < dest.len() { - let ctrs = [(ctr >> 24) as u8, (ctr >> 16) as u8, (ctr >> 8) as u8, ctr as u8]; - hasher.input(&ctrs); - hasher.input(secret); - hasher.input(s1); - hasher.result(&mut dest[written..(written + 32)]); - hasher.reset(); - written += 32; - ctr += 1; - } - } -} - -#[cfg(test)] -mod tests { - use ethkey::{Random, Generator}; - use ecies; - - #[test] - fn ecies_shared() { - let kp = Random.generate().unwrap(); - let message = b"So many books, so little time"; - - let shared = b"shared"; - let wrong_shared = b"incorrect"; - let encrypted = ecies::encrypt(kp.public(), shared, message).unwrap(); - assert!(encrypted[..] != message[..]); - assert_eq!(encrypted[0], 0x04); - - assert!(ecies::decrypt(kp.secret(), wrong_shared, &encrypted).is_err()); - let decrypted = ecies::decrypt(kp.secret(), shared, &encrypted).unwrap(); - assert_eq!(decrypted[..message.len()], message[..]); - } -} - diff --git a/ethkey/Cargo.toml b/ethkey/Cargo.toml index a3e903271beab2208671c744479261ac170424af..952354739d52750f256f207d3659307877e6e3fc 100644 --- a/ethkey/Cargo.toml +++ b/ethkey/Cargo.toml @@ -6,12 +6,14 @@ authors = ["Parity Technologies "] [dependencies] byteorder = "1.0" edit-distance = "2.0" +ethcore-crypto = { path = "../ethcore/crypto" } eth-secp256k1 = { git = "https://github.com/paritytech/rust-secp256k1" } -ethereum-types = "0.2" +ethereum-types = "0.3" lazy_static = "1.0" log = "0.3" +mem = { path = "../util/mem" } parity-wordlist = "1.2" +quick-error = "1.2" rand = "0.4" -rust-crypto = "0.2" rustc-hex = "1.0" -tiny-keccak = "1.3" +tiny-keccak = "1.4" diff --git a/ethkey/cli/src/main.rs b/ethkey/cli/src/main.rs index 9298788f069f6647a193089af055618decb9984d..af50f86b31a01620c580e61846f3774682cf6d8e 100644 --- a/ethkey/cli/src/main.rs +++ b/ethkey/cli/src/main.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -166,10 +166,11 @@ fn main() { match execute(env::args()) { Ok(ok) => println!("{}", ok), + Err(Error::Docopt(ref e)) => e.exit(), Err(err) => { println!("{}", err); process::exit(1); - }, + } } } @@ -180,9 +181,9 @@ fn display(result: (KeyPair, Option), mode: DisplayMode) -> String { Some(extra_data) => format!("{}\n{}", extra_data, keypair), None => format!("{}", keypair) }, - DisplayMode::Secret => format!("{:?}", keypair.secret()), - DisplayMode::Public => format!("{:?}", keypair.public()), - DisplayMode::Address => format!("{:?}", keypair.address()), + DisplayMode::Secret => format!("{:x}", keypair.secret()), + DisplayMode::Public => format!("{:x}", keypair.public()), + DisplayMode::Address => format!("{:x}", keypair.address()), } } diff --git a/ethkey/src/brain.rs b/ethkey/src/brain.rs index fffae0bed8e08ba9955e502969ec37062cd281ed..55b525e2a4137866d9d3fe12e9b21d92a7336081 100644 --- a/ethkey/src/brain.rs +++ b/ethkey/src/brain.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethkey/src/brain_prefix.rs b/ethkey/src/brain_prefix.rs index a4e31e989c969d063dfb36ad600700479deca63a..accf9473700e0630eead6821f355e7a7f20fb36d 100644 --- a/ethkey/src/brain_prefix.rs +++ b/ethkey/src/brain_prefix.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethkey/src/brain_recover.rs b/ethkey/src/brain_recover.rs index f064c6fd0e1bc6bf9dcc1f4134faf69ef09c66c2..51331932328bcd9c1aea6a47393db244d8acee4c 100644 --- a/ethkey/src/brain_recover.rs +++ b/ethkey/src/brain_recover.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -21,7 +21,6 @@ use parity_wordlist; use super::{Address, Brain, Generator}; - /// Tries to find a phrase for address, given the number /// of expected words and a partial phrase. /// @@ -150,7 +149,6 @@ impl Iterator for PhrasesIterator { mod tests { use super::PhrasesIterator; - #[test] fn should_generate_possible_combinations() { let mut it = PhrasesIterator::new(vec![ diff --git a/ethkey/src/crypto.rs b/ethkey/src/crypto.rs new file mode 100644 index 0000000000000000000000000000000000000000..3ff809614ef5330db031bada6d0fca2b38d0fa61 --- /dev/null +++ b/ethkey/src/crypto.rs @@ -0,0 +1,189 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use secp256k1; +use std::io; +use ethcore_crypto::error::SymmError; + +quick_error! { + #[derive(Debug)] + pub enum Error { + Secp(e: secp256k1::Error) { + display("secp256k1 error: {}", e) + cause(e) + from() + } + Io(e: io::Error) { + display("i/o error: {}", e) + cause(e) + from() + } + InvalidMessage { + display("invalid message") + } + Symm(e: SymmError) { + cause(e) + from() + } + } +} + +/// ECDH functions +pub mod ecdh { + use secp256k1::{self, ecdh, key}; + use super::Error; + use {Secret, Public, SECP256K1}; + + /// Agree on a shared secret + pub fn agree(secret: &Secret, public: &Public) -> Result { + let context = &SECP256K1; + let pdata = { + let mut temp = [4u8; 65]; + (&mut temp[1..65]).copy_from_slice(&public[0..64]); + temp + }; + + let publ = key::PublicKey::from_slice(context, &pdata)?; + let sec = key::SecretKey::from_slice(context, &secret)?; + let shared = ecdh::SharedSecret::new_raw(context, &publ, &sec); + + Secret::from_unsafe_slice(&shared[0..32]) + .map_err(|_| Error::Secp(secp256k1::Error::InvalidSecretKey)) + } +} + +/// ECIES function +pub mod ecies { + use ethcore_crypto::{aes, digest, hmac, is_equal}; + use ethereum_types::H128; + use super::{ecdh, Error}; + use {Random, Generator, Public, Secret}; + + /// Encrypt a message with a public key, writing an HMAC covering both + /// the plaintext and authenticated data. + /// + /// Authenticated data may be empty. + pub fn encrypt(public: &Public, auth_data: &[u8], plain: &[u8]) -> Result, Error> { + let r = Random.generate()?; + let z = ecdh::agree(r.secret(), public)?; + let mut key = [0u8; 32]; + kdf(&z, &[0u8; 0], &mut key); + + let ekey = &key[0..16]; + let mkey = hmac::SigKey::sha256(&digest::sha256(&key[16..32])); + + let mut msg = vec![0u8; 1 + 64 + 16 + plain.len() + 32]; + msg[0] = 0x04u8; + { + let msgd = &mut msg[1..]; + msgd[0..64].copy_from_slice(r.public()); + let iv = H128::random(); + msgd[64..80].copy_from_slice(&iv); + { + let cipher = &mut msgd[(64 + 16)..(64 + 16 + plain.len())]; + aes::encrypt_128_ctr(ekey, &iv, plain, cipher)?; + } + let mut hmac = hmac::Signer::with(&mkey); + { + let cipher_iv = &msgd[64..(64 + 16 + plain.len())]; + hmac.update(cipher_iv); + } + hmac.update(auth_data); + let sig = hmac.sign(); + msgd[(64 + 16 + plain.len())..].copy_from_slice(&sig); + } + Ok(msg) + } + + /// Decrypt a message with a secret key, checking HMAC for ciphertext + /// and authenticated data validity. + pub fn decrypt(secret: &Secret, auth_data: &[u8], encrypted: &[u8]) -> Result, Error> { + let meta_len = 1 + 64 + 16 + 32; + if encrypted.len() < meta_len || encrypted[0] < 2 || encrypted[0] > 4 { + return Err(Error::InvalidMessage); //invalid message: publickey + } + + let e = &encrypted[1..]; + let p = Public::from_slice(&e[0..64]); + let z = ecdh::agree(secret, &p)?; + let mut key = [0u8; 32]; + kdf(&z, &[0u8; 0], &mut key); + + let ekey = &key[0..16]; + let mkey = hmac::SigKey::sha256(&digest::sha256(&key[16..32])); + + let clen = encrypted.len() - meta_len; + let cipher_with_iv = &e[64..(64+16+clen)]; + let cipher_iv = &cipher_with_iv[0..16]; + let cipher_no_iv = &cipher_with_iv[16..]; + let msg_mac = &e[(64+16+clen)..]; + + // Verify tag + let mut hmac = hmac::Signer::with(&mkey); + hmac.update(cipher_with_iv); + hmac.update(auth_data); + let mac = hmac.sign(); + + if !is_equal(&mac.as_ref()[..], msg_mac) { + return Err(Error::InvalidMessage); + } + + let mut msg = vec![0u8; clen]; + aes::decrypt_128_ctr(ekey, cipher_iv, cipher_no_iv, &mut msg[..])?; + Ok(msg) + } + + fn kdf(secret: &Secret, s1: &[u8], dest: &mut [u8]) { + // SEC/ISO/Shoup specify counter size SHOULD be equivalent + // to size of hash output, however, it also notes that + // the 4 bytes is okay. NIST specifies 4 bytes. + let mut ctr = 1u32; + let mut written = 0usize; + while written < dest.len() { + let mut hasher = digest::Hasher::sha256(); + let ctrs = [(ctr >> 24) as u8, (ctr >> 16) as u8, (ctr >> 8) as u8, ctr as u8]; + hasher.update(&ctrs); + hasher.update(secret); + hasher.update(s1); + let d = hasher.finish(); + &mut dest[written..(written + 32)].copy_from_slice(&d); + written += 32; + ctr += 1; + } + } +} + +#[cfg(test)] +mod tests { + use super::ecies; + use {Random, Generator}; + + #[test] + fn ecies_shared() { + let kp = Random.generate().unwrap(); + let message = b"So many books, so little time"; + + let shared = b"shared"; + let wrong_shared = b"incorrect"; + let encrypted = ecies::encrypt(kp.public(), shared, message).unwrap(); + assert!(encrypted[..] != message[..]); + assert_eq!(encrypted[0], 0x04); + + assert!(ecies::decrypt(kp.secret(), wrong_shared, &encrypted).is_err()); + let decrypted = ecies::decrypt(kp.secret(), shared, &encrypted).unwrap(); + assert_eq!(decrypted[..message.len()], message[..]); + } +} diff --git a/ethkey/src/error.rs b/ethkey/src/error.rs index 0ac2d221a9dc4561730a800c9cfd04d2478b4696..7cba375d0f2690af20869c8601a5e5199103b2ab 100644 --- a/ethkey/src/error.rs +++ b/ethkey/src/error.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use std::fmt; +use std::{fmt, error}; #[derive(Debug)] /// Crypto error @@ -51,6 +51,12 @@ impl fmt::Display for Error { } } +impl error::Error for Error { + fn description(&self) -> &str { + "Crypto error" + } +} + impl Into for Error { fn into(self) -> String { format!("{}", self) diff --git a/ethkey/src/extended.rs b/ethkey/src/extended.rs index 23aff0bee8ea3b549e8013baf414466ffe911385..89a4bb26a021d74f95bcac7e4a3a50e17115194b 100644 --- a/ethkey/src/extended.rs +++ b/ethkey/src/extended.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -99,7 +99,7 @@ impl ExtendedSecret { pub fn derive(&self, index: Derivation) -> ExtendedSecret where T: Label { let (derived_key, next_chain_code) = derivation::private(*self.secret, self.chain_code, index); - let derived_secret = Secret::from_slice(&*derived_key); + let derived_secret = Secret::from(derived_key.0); ExtendedSecret::with_code(derived_secret, next_chain_code) } @@ -207,9 +207,7 @@ impl ExtendedKeyPair { // Work is based on BIP0032 // https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki mod derivation { - use rcrypto::hmac::Hmac; - use rcrypto::mac::Mac; - use rcrypto::sha2::Sha512; + use ethcore_crypto::hmac; use ethereum_types::{U256, U512, H512, H256}; use secp256k1::key::{SecretKey, PublicKey}; use SECP256K1; @@ -242,10 +240,8 @@ mod derivation { let private: U256 = private_key.into(); // produces 512-bit derived hmac (I) - let mut hmac = Hmac::new(Sha512::new(), &*chain_code); - let mut i_512 = [0u8; 64]; - hmac.input(&data[..]); - hmac.raw_result(&mut i_512); + let skey = hmac::SigKey::sha512(&*chain_code); + let i_512 = hmac::sign(&skey, &data[..]); // left most 256 bits are later added to original private key let hmac_key: U256 = H256::from_slice(&i_512[0..32]).into(); @@ -321,10 +317,8 @@ mod derivation { index.store(&mut data[33..(33 + T::len())]); // HMAC512SHA produces [derived private(256); new chain code(256)] - let mut hmac = Hmac::new(Sha512::new(), &*chain_code); - let mut i_512 = [0u8; 64]; - hmac.input(&data[..]); - hmac.raw_result(&mut i_512); + let skey = hmac::SigKey::sha512(&*chain_code); + let i_512 = hmac::sign(&skey, &data[..]); let new_private = H256::from(&i_512[0..32]); let new_chain_code = H256::from(&i_512[32..64]); @@ -369,10 +363,8 @@ mod derivation { } pub fn seed_pair(seed: &[u8]) -> (H256, H256) { - let mut hmac = Hmac::new(Sha512::new(), b"Bitcoin seed"); - let mut i_512 = [0u8; 64]; - hmac.input(seed); - hmac.raw_result(&mut i_512); + let skey = hmac::SigKey::sha512(b"Bitcoin seed"); + let i_512 = hmac::sign(&skey, seed); let master_key = H256::from_slice(&i_512[0..32]); let chain_code = H256::from_slice(&i_512[32..64]); @@ -399,7 +391,7 @@ mod tests { fn test_extended(f: F, test_private: H256) where F: Fn(ExtendedSecret) -> ExtendedSecret { let (private_seed, chain_code) = master_chain_basic(); - let extended_secret = ExtendedSecret::with_code(Secret::from_slice(&*private_seed), chain_code); + let extended_secret = ExtendedSecret::with_code(Secret::from(private_seed.0), chain_code); let derived = f(extended_secret); assert_eq!(**derived.as_raw(), test_private); } diff --git a/ethkey/src/keccak.rs b/ethkey/src/keccak.rs index 002f20d9468fbcde383421b8e03e71025ae8409f..3801d841ab0cbc4dfe5c33bee3c26d460e6d3fcc 100644 --- a/ethkey/src/keccak.rs +++ b/ethkey/src/keccak.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethkey/src/keypair.rs b/ethkey/src/keypair.rs index 5a13d476bb329e8128fb1f4d7744b6960139d0c7..610c14524fefd6ebb1e33a536f840521433134ab 100644 --- a/ethkey/src/keypair.rs +++ b/ethkey/src/keypair.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethkey/src/lib.rs b/ethkey/src/lib.rs index 09a100b74cd93851267abd5009808d97054064b4..7aec015c477cd614d72f71b5a142599b28b486a7 100644 --- a/ethkey/src/lib.rs +++ b/ethkey/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -17,10 +17,13 @@ // #![warn(missing_docs)] extern crate byteorder; -extern crate crypto as rcrypto; extern crate edit_distance; +extern crate ethcore_crypto; extern crate ethereum_types; +extern crate mem; extern crate parity_wordlist; +#[macro_use] +extern crate quick_error; extern crate rand; extern crate rustc_hex; extern crate secp256k1; @@ -43,6 +46,7 @@ mod secret; mod extended; pub mod brain_recover; +pub mod crypto; pub mod math; pub use self::parity_wordlist::Error as WordlistError; diff --git a/ethkey/src/math.rs b/ethkey/src/math.rs index e2426b4fbdff3b37a3166276c8a18cca62b2f41d..6b1d4013bd7d534f6ee4d762eb4d65872a20b2d0 100644 --- a/ethkey/src/math.rs +++ b/ethkey/src/math.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethkey/src/prefix.rs b/ethkey/src/prefix.rs index f2ef0f0ffb4610d05cd37e2d9ca3f59e25cdfe58..2668050ef8e8ac3735393a9bdb85f01dfabd9e8c 100644 --- a/ethkey/src/prefix.rs +++ b/ethkey/src/prefix.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethkey/src/random.rs b/ethkey/src/random.rs index b44a4b2ca8015acef2b72882e1f8cafc03fc9fa9..d42bb4ea4df1c4f3e43b0fe08bf7a0c009d31e17 100644 --- a/ethkey/src/random.rs +++ b/ethkey/src/random.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethkey/src/secret.rs b/ethkey/src/secret.rs index 6e0548264f934fc43ce9ba97842799ef3c2b0234..a3560698af4a02b78e61cb349b433a5e9be849ac 100644 --- a/ethkey/src/secret.rs +++ b/ethkey/src/secret.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -18,24 +18,32 @@ use std::fmt; use std::ops::Deref; use std::str::FromStr; use rustc_hex::ToHex; +use secp256k1::constants::{SECRET_KEY_SIZE as SECP256K1_SECRET_KEY_SIZE}; use secp256k1::key; use ethereum_types::H256; +use mem::Memzero; use {Error, SECP256K1}; #[derive(Clone, PartialEq, Eq)] pub struct Secret { - inner: H256, + inner: Memzero, } impl ToHex for Secret { fn to_hex(&self) -> String { - self.inner.to_hex() + format!("{:x}", *self.inner) + } +} + +impl fmt::LowerHex for Secret { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + self.inner.fmt(fmt) } } impl fmt::Debug for Secret { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - write!(fmt, "{:?}", self.inner) + self.inner.fmt(fmt) } } @@ -46,17 +54,19 @@ impl fmt::Display for Secret { } impl Secret { - pub fn from_slice(key: &[u8]) -> Self { - assert_eq!(32, key.len(), "Caller should provide 32-byte length slice"); - + /// Creates a `Secret` from the given slice, returning `None` if the slice length != 32. + pub fn from_slice(key: &[u8]) -> Option { + if key.len() != 32 { + return None + } let mut h = H256::default(); h.copy_from_slice(&key[0..32]); - Secret { inner: h } + Some(Secret { inner: Memzero::from(h) }) } /// Creates zero key, which is invalid for crypto operations, but valid for math operation. pub fn zero() -> Self { - Secret { inner: Default::default() } + Secret { inner: Memzero::from(H256::default()) } } /// Imports and validates the key. @@ -202,9 +212,15 @@ impl FromStr for Secret { } } +impl From<[u8; 32]> for Secret { + fn from(k: [u8; 32]) -> Self { + Secret { inner: Memzero::from(H256(k)) } + } +} + impl From for Secret { fn from(s: H256) -> Self { - Secret::from_slice(&s) + s.0.into() } } @@ -216,7 +232,9 @@ impl From<&'static str> for Secret { impl From for Secret { fn from(key: key::SecretKey) -> Self { - Self::from_slice(&key[0..32]) + let mut a = [0; SECP256K1_SECRET_KEY_SIZE]; + a.copy_from_slice(&key[0 .. SECP256K1_SECRET_KEY_SIZE]); + a.into() } } diff --git a/ethkey/src/signature.rs b/ethkey/src/signature.rs index ec225ec011b3d78ed743d7573894401169ef868e..cd6d88fe18736847792de6406892c61cff2d715f 100644 --- a/ethkey/src/signature.rs +++ b/ethkey/src/signature.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethstore/Cargo.toml b/ethstore/Cargo.toml old mode 100755 new mode 100644 index a504c7f2a55fc9a3449a0c6c431223e6e4956705..6108143cb9e9c239341738212c9c6508075beb7d --- a/ethstore/Cargo.toml +++ b/ethstore/Cargo.toml @@ -12,17 +12,15 @@ serde = "1.0" serde_json = "1.0" serde_derive = "1.0" rustc-hex = "1.0" -rust-crypto = "0.2.36" -tiny-keccak = "1.3" +tiny-keccak = "1.4" time = "0.1.34" itertools = "0.5" parking_lot = "0.5" -ethcrypto = { path = "../ethcrypto" } -ethereum-types = "0.2" +ethcore-crypto = { path = "../ethcore/crypto" } +ethereum-types = "0.3" dir = { path = "../util/dir" } smallvec = "0.4" parity-wordlist = "1.0" -subtle = "0.5" tempdir = "0.3" [dev-dependencies] diff --git a/ethstore/cli/Cargo.toml b/ethstore/cli/Cargo.toml index f164f0a6722ce217bd779fce4c93f4e67af3f3c7..bd5db86216f111d16648392b1edfb04d3d89db30 100644 --- a/ethstore/cli/Cargo.toml +++ b/ethstore/cli/Cargo.toml @@ -18,3 +18,6 @@ panic_hook = { path = "../../util/panic_hook" } name = "ethstore" path = "src/main.rs" doc = false + +[dev-dependencies] +tempdir = "0.3.5" diff --git a/ethstore/cli/src/crack.rs b/ethstore/cli/src/crack.rs index 64eda66e56ed378ed10fb9d9a92377aee03af1bd..3e767a6084fd049b0af709c4b694fdb0ce85baa4 100644 --- a/ethstore/cli/src/crack.rs +++ b/ethstore/cli/src/crack.rs @@ -1,3 +1,19 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + use std::{cmp, thread}; use std::sync::Arc; use std::collections::VecDeque; diff --git a/ethstore/cli/src/main.rs b/ethstore/cli/src/main.rs index 45f9f6920ee548599c11c28e2dfdc89f3882a98d..922d857149d61d35938af65aee08589621b31485 100644 --- a/ethstore/cli/src/main.rs +++ b/ethstore/cli/src/main.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -149,6 +149,7 @@ fn main() { match execute(env::args()) { Ok(result) => println!("{}", result), + Err(Error::Docopt(ref e)) => e.exit(), Err(err) => { println!("{}", err); process::exit(1); @@ -191,7 +192,7 @@ fn open_args_vault_account(store: &EthStore, address: Address, args: &Args) -> R fn format_accounts(accounts: &[Address]) -> String { accounts.iter() .enumerate() - .map(|(i, a)| format!("{:2}: 0x{:?}", i, a)) + .map(|(i, a)| format!("{:2}: 0x{:x}", i, a)) .collect::>() .join("\n") } @@ -220,7 +221,7 @@ fn execute(command: I) -> Result where I: IntoIterator(command: I) -> Result where I: IntoIterator>(); @@ -272,7 +273,7 @@ fn execute(command: I) -> Result where I: IntoIterator. + +extern crate tempdir; +use std::process::Command; +use tempdir::TempDir; +use std::fs::File; +use std::io::Write; + +fn run(args: &[&str]) -> String { + let output = Command::new("cargo") + .args(&["run", "--"]) + .args(args) + .output() + .unwrap(); + assert!(output.status.success()); + String::from_utf8(output.stdout).unwrap() +} + +#[test] +fn cli_cmd() { + Command::new("cargo") + .arg("build") + .output() + .unwrap(); + + let dir = TempDir::new("test-vault").unwrap(); + + let mut passwd = File::create(dir.path().join("test-password")).unwrap(); + writeln!(passwd, "password").unwrap(); + + let mut passwd2 = File::create(dir.path().join("test-vault-addr")).unwrap(); + writeln!(passwd2, "password2").unwrap(); + + let test_password_buf = dir.path().join("test-password"); + let test_password: &str = test_password_buf.to_str().unwrap(); + let dir_str: &str = dir.path().to_str().unwrap(); + let test_vault_addr_buf = dir.path().join("test-vault-addr"); + let test_vault_addr = test_vault_addr_buf.to_str().unwrap(); + + run(&["create-vault", "test-vault", test_password, "--dir", dir_str]); + + let output = run(&["insert", "7d29fab185a33e2cd955812397354c472d2b84615b645aa135ff539f6b0d70d5", + test_vault_addr, + "--dir", dir_str, + "--vault", "test-vault", + "--vault-pwd", test_password]); + let address = output.trim(); + + let output = run(&["list", + "--dir", dir_str, + "--vault", "test-vault", + "--vault-pwd", test_password]); + assert_eq!(output, " 0: 0xa8fa5dd30a87bb9e3288d604eb74949c515ab66e\n"); + + let output = run(&["sign", &address[2..], + test_vault_addr, + "7d29fab185a33e2cd955812397354c472d2b84615b645aa135ff539f6b0d70d5", + "--dir", dir_str, + "--vault", "test-vault", + "--vault-pwd", test_password]); + assert_eq!(output, "0x54ab6e5cf0c5cb40043fdca5d15d611a3a94285414a076dafecc8dc9c04183f413296a3defff61092c0bb478dc9887ec01070e1275234211208fb8f4be4a9b0101\n"); + + let output = run(&["public", &address[2..], test_vault_addr, + "--dir", dir_str, + "--vault", "test-vault", + "--vault-pwd", test_password]); + assert_eq!(output, "0x35f222d88b80151857a2877826d940104887376a94c1cbd2c8c7c192eb701df88a18a4ecb8b05b1466c5b3706042027b5e079fe3a3683e66d822b0e047aa3418\n"); +} diff --git a/ethstore/src/account/cipher.rs b/ethstore/src/account/cipher.rs index 427ccafc4a01a6bf99111a558ea3252e546ce1da..92a5304edb9231ccd1fbc886bb9604c2dd66fcf1 100644 --- a/ethstore/src/account/cipher.rs +++ b/ethstore/src/account/cipher.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethstore/src/account/crypto.rs b/ethstore/src/account/crypto.rs old mode 100755 new mode 100644 index 967c92306ae154165553404ce534aabcb553a447..3143958a12937a6e6ac795dac0576fd98cfbaa74 --- a/ethstore/src/account/crypto.rs +++ b/ethstore/src/account/crypto.rs @@ -1,4 +1,4 @@ -// Copyright 2015, 2016, 2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -21,7 +21,6 @@ use crypto::Keccak256; use random::Random; use smallvec::SmallVec; use account::{Cipher, Kdf, Aes128Ctr, Pbkdf2, Prf}; -use subtle; /// Encrypted data #[derive(Debug, PartialEq, Clone)] @@ -74,12 +73,12 @@ impl From for String { impl Crypto { /// Encrypt account secret - pub fn with_secret(secret: &Secret, password: &str, iterations: u32) -> Self { + pub fn with_secret(secret: &Secret, password: &str, iterations: u32) -> Result { Crypto::with_plain(&*secret, password, iterations) } /// Encrypt custom plain data - pub fn with_plain(plain: &[u8], password: &str, iterations: u32) -> Self { + pub fn with_plain(plain: &[u8], password: &str, iterations: u32) -> Result { let salt: [u8; 32] = Random::random(); let iv: [u8; 16] = Random::random(); @@ -93,12 +92,12 @@ impl Crypto { let mut ciphertext: SmallVec<[u8; 32]> = SmallVec::from_vec(vec![0; plain_len]); // aes-128-ctr with initial vector of iv - crypto::aes::encrypt(&derived_left_bits, &iv, plain, &mut *ciphertext); + crypto::aes::encrypt_128_ctr(&derived_left_bits, &iv, plain, &mut *ciphertext)?; // KECCAK(DK[16..31] ++ ), where DK[16..31] - derived_right_bits let mac = crypto::derive_mac(&derived_right_bits, &*ciphertext).keccak256(); - Crypto { + Ok(Crypto { cipher: Cipher::Aes128Ctr(Aes128Ctr { iv: iv, }), @@ -110,7 +109,7 @@ impl Crypto { prf: Prf::HmacSha256, }), mac: mac, - } + }) } /// Try to decrypt and convert result to account secret @@ -132,13 +131,13 @@ impl Crypto { fn do_decrypt(&self, password: &str, expected_len: usize) -> Result, Error> { let (derived_left_bits, derived_right_bits) = match self.kdf { Kdf::Pbkdf2(ref params) => crypto::derive_key_iterations(password, ¶ms.salt, params.c), - Kdf::Scrypt(ref params) => crypto::derive_key_scrypt(password, ¶ms.salt, params.n, params.p, params.r)?, + Kdf::Scrypt(ref params) => crypto::scrypt::derive_key(password, ¶ms.salt, params.n, params.p, params.r)?, }; let mac = crypto::derive_mac(&derived_right_bits, &self.ciphertext).keccak256(); - if subtle::slices_equal(&mac, &self.mac) == 0 { - return Err(Error::InvalidPassword); + if !crypto::is_equal(&mac, &self.mac) { + return Err(Error::InvalidPassword) } let mut plain: SmallVec<[u8; 32]> = SmallVec::from_vec(vec![0; expected_len]); @@ -149,7 +148,7 @@ impl Crypto { debug_assert!(expected_len >= self.ciphertext.len()); let from = expected_len - self.ciphertext.len(); - crypto::aes::decrypt(&derived_left_bits, ¶ms.iv, &self.ciphertext, &mut plain[from..]); + crypto::aes::decrypt_128_ctr(&derived_left_bits, ¶ms.iv, &self.ciphertext, &mut plain[from..])?; Ok(plain.into_iter().collect()) }, } @@ -164,7 +163,7 @@ mod tests { #[test] fn crypto_with_secret_create() { let keypair = Random.generate().unwrap(); - let crypto = Crypto::with_secret(keypair.secret(), "this is sparta", 10240); + let crypto = Crypto::with_secret(keypair.secret(), "this is sparta", 10240).unwrap(); let secret = crypto.secret("this is sparta").unwrap(); assert_eq!(keypair.secret(), &secret); } @@ -172,14 +171,14 @@ mod tests { #[test] fn crypto_with_secret_invalid_password() { let keypair = Random.generate().unwrap(); - let crypto = Crypto::with_secret(keypair.secret(), "this is sparta", 10240); + let crypto = Crypto::with_secret(keypair.secret(), "this is sparta", 10240).unwrap(); assert_matches!(crypto.secret("this is sparta!"), Err(Error::InvalidPassword)) } #[test] fn crypto_with_null_plain_data() { let original_data = b""; - let crypto = Crypto::with_plain(&original_data[..], "this is sparta", 10240); + let crypto = Crypto::with_plain(&original_data[..], "this is sparta", 10240).unwrap(); let decrypted_data = crypto.decrypt("this is sparta").unwrap(); assert_eq!(original_data[..], *decrypted_data); } @@ -187,7 +186,7 @@ mod tests { #[test] fn crypto_with_tiny_plain_data() { let original_data = b"{}"; - let crypto = Crypto::with_plain(&original_data[..], "this is sparta", 10240); + let crypto = Crypto::with_plain(&original_data[..], "this is sparta", 10240).unwrap(); let decrypted_data = crypto.decrypt("this is sparta").unwrap(); assert_eq!(original_data[..], *decrypted_data); } @@ -195,7 +194,7 @@ mod tests { #[test] fn crypto_with_huge_plain_data() { let original_data: Vec<_> = (1..65536).map(|i| (i % 256) as u8).collect(); - let crypto = Crypto::with_plain(&original_data, "this is sparta", 10240); + let crypto = Crypto::with_plain(&original_data, "this is sparta", 10240).unwrap(); let decrypted_data = crypto.decrypt("this is sparta").unwrap(); assert_eq!(&original_data, &decrypted_data); } diff --git a/ethstore/src/account/kdf.rs b/ethstore/src/account/kdf.rs index 31b8f304ca63b25972b5dcc876724c33d03737d8..4d6d7cd956df3c6b65dcf10be2a83822becef356 100644 --- a/ethstore/src/account/kdf.rs +++ b/ethstore/src/account/kdf.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethstore/src/account/mod.rs b/ethstore/src/account/mod.rs old mode 100755 new mode 100644 index c352ffe78fcb8c3eb5ec4bcd271af310a7d7574e..e13237d827056d886cf9019c29aaf93fcc30dcbf --- a/ethstore/src/account/mod.rs +++ b/ethstore/src/account/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -25,4 +25,3 @@ pub use self::crypto::Crypto; pub use self::kdf::{Kdf, Pbkdf2, Scrypt, Prf}; pub use self::safe_account::SafeAccount; pub use self::version::Version; - diff --git a/ethstore/src/account/safe_account.rs b/ethstore/src/account/safe_account.rs old mode 100755 new mode 100644 index 478b796e6462b8d854a29dc9c3d29d59a9909982..0bda99d02c0caa53293675b701ddf3d15d5c74f3 --- a/ethstore/src/account/safe_account.rs +++ b/ethstore/src/account/safe_account.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -14,10 +14,11 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use ethkey::{KeyPair, sign, Address, Signature, Message, Public, Secret}; -use crypto::ecdh::agree; -use {json, Error, crypto}; +use ethkey::{self, KeyPair, sign, Address, Signature, Message, Public, Secret}; +use ethkey::crypto::ecdh::agree; +use {json, Error}; use account::Version; +use crypto; use super::crypto::Crypto; /// Account representation. @@ -61,16 +62,16 @@ impl SafeAccount { iterations: u32, name: String, meta: String - ) -> Self { - SafeAccount { + ) -> Result { + Ok(SafeAccount { id: id, version: Version::V3, - crypto: Crypto::with_secret(keypair.secret(), password, iterations), + 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 @@ -114,7 +115,7 @@ impl SafeAccount { 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); + let meta_crypto = Crypto::with_plain(&meta_plain, password, iterations)?; Ok(json::VaultKeyFile { id: self.id.into(), @@ -133,7 +134,7 @@ impl SafeAccount { /// Decrypt a message. pub fn decrypt(&self, password: &str, shared_mac: &[u8], message: &[u8]) -> Result, Error> { let secret = self.crypto.secret(password)?; - crypto::ecies::decrypt(&secret, shared_mac, message).map_err(From::from) + ethkey::crypto::ecies::decrypt(&secret, shared_mac, message).map_err(From::from) } /// Agree on shared key. @@ -154,7 +155,7 @@ impl SafeAccount { let result = SafeAccount { id: self.id.clone(), version: self.version.clone(), - crypto: Crypto::with_secret(&secret, new_password, iterations), + crypto: Crypto::with_secret(&secret, new_password, iterations)?, address: self.address.clone(), filename: self.filename.clone(), name: self.name.clone(), @@ -180,7 +181,7 @@ mod tests { let password = "hello world"; let message = Message::default(); let account = SafeAccount::create(&keypair, [0u8; 16], password, 10240, "Test".to_owned(), "{}".to_owned()); - let signature = account.sign(password, &message).unwrap(); + let signature = account.unwrap().sign(password, &message).unwrap(); assert!(verify_public(keypair.public(), &signature, &message).unwrap()); } @@ -191,7 +192,7 @@ mod tests { let sec_password = "this is sparta"; let i = 10240; let message = Message::default(); - let account = SafeAccount::create(&keypair, [0u8; 16], first_password, i, "Test".to_owned(), "{}".to_owned()); + 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()); diff --git a/ethstore/src/account/version.rs b/ethstore/src/account/version.rs index 2ba0848a682ecef79d64c0e95fb8f38ea55a79f2..d206a2c12d78efbe5e89f55f6018c67c01791869 100644 --- a/ethstore/src/account/version.rs +++ b/ethstore/src/account/version.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethstore/src/accounts_dir/disk.rs b/ethstore/src/accounts_dir/disk.rs old mode 100755 new mode 100644 index 1446118440667bcce4afec375e4e45a4009f6d39..a9253cff06fa4a289eccb55d6fa9e8c095f3ab7e --- a/ethstore/src/accounts_dir/disk.rs +++ b/ethstore/src/accounts_dir/disk.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -33,22 +33,43 @@ const IGNORED_FILES: &'static [&'static str] = &[ "vault.json", ]; -#[cfg(not(windows))] -fn restrict_permissions_to_owner(file_path: &Path) -> Result<(), i32> { - use std::ffi; + +#[cfg(unix)] +fn create_new_file_with_permissions_to_owner(file_path: &Path) -> io::Result { use libc; + use std::os::unix::fs::OpenOptionsExt; - let cstr = ffi::CString::new(&*file_path.to_string_lossy()) - .map_err(|_| -1)?; - match unsafe { libc::chmod(cstr.as_ptr(), libc::S_IWUSR | libc::S_IRUSR) } { - 0 => Ok(()), - x => Err(x), - } + fs::OpenOptions::new() + .write(true) + .create_new(true) + .mode((libc::S_IWUSR | libc::S_IRUSR) as u32) + .open(file_path) +} + +#[cfg(not(unix))] +fn create_new_file_with_permissions_to_owner(file_path: &Path) -> io::Result { + fs::OpenOptions::new() + .write(true) + .create_new(true) + .open(file_path) } -#[cfg(windows)] -fn restrict_permissions_to_owner(_file_path: &Path) -> Result<(), i32> { - Ok(()) +#[cfg(unix)] +fn replace_file_with_permissions_to_owner(file_path: &Path) -> io::Result { + use libc; + 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) +} + +#[cfg(not(unix))] +fn replace_file_with_permissions_to_owner(file_path: &Path) -> io::Result { + fs::File::create(file_path) } /// Root keys directory implementation @@ -153,7 +174,6 @@ impl DiskDirectory where T: KeyFileManager { ) } - /// 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 { @@ -174,17 +194,16 @@ impl DiskDirectory where T: KeyFileManager { { // save the file - let mut file = fs::File::create(&keyfile_path)?; + 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()?; - - if let Err(_) = restrict_permissions_to_owner(keyfile_path.as_path()) { - return Err(Error::Io(io::Error::last_os_error())); - } - file.sync_all()?; } @@ -319,7 +338,7 @@ mod test { // when let account = SafeAccount::create(&keypair, [0u8; 16], password, 1024, "Test".to_owned(), "{}".to_owned()); - let res = directory.insert(account); + let res = directory.insert(account.unwrap()); // then assert!(res.is_ok(), "Should save account succesfuly."); @@ -339,7 +358,7 @@ mod test { let directory = RootDiskDirectory::create(dir.clone()).unwrap(); // when - let account = SafeAccount::create(&keypair, [0u8; 16], password, 1024, "Test".to_owned(), "{}".to_owned()); + let account = SafeAccount::create(&keypair, [0u8; 16], password, 1024, "Test".to_owned(), "{}".to_owned()).unwrap(); let filename = "test".to_string(); let dedup = true; @@ -424,7 +443,7 @@ mod test { let keypair = Random.generate().unwrap(); let password = "test pass"; let account = SafeAccount::create(&keypair, [0u8; 16], password, 1024, "Test".to_owned(), "{}".to_owned()); - directory.insert(account).expect("Account should be inserted ok"); + directory.insert(account.unwrap()).expect("Account should be inserted ok"); let new_hash = directory.files_hash().expect("New files hash should be calculated ok"); diff --git a/ethstore/src/accounts_dir/memory.rs b/ethstore/src/accounts_dir/memory.rs index bd162b5ef0ce7ca7b4360c323878b32b3cd8634d..71ddfa536e3f086e31f2149a7230e6cd56b7f724 100644 --- a/ethstore/src/accounts_dir/memory.rs +++ b/ethstore/src/accounts_dir/memory.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -16,7 +16,7 @@ use std::collections::HashMap; use parking_lot::RwLock; -use itertools::Itertools; +use itertools; use ethkey::Address; use {SafeAccount, Error}; @@ -30,7 +30,7 @@ pub struct MemoryDirectory { impl KeyDirectory for MemoryDirectory { fn load(&self) -> Result, Error> { - Ok(self.accounts.read().values().cloned().flatten().collect()) + Ok(itertools::Itertools::flatten(self.accounts.read().values().cloned()).collect()) } fn update(&self, account: SafeAccount) -> Result { @@ -72,4 +72,3 @@ impl KeyDirectory for MemoryDirectory { Ok(val) } } - diff --git a/ethstore/src/accounts_dir/mod.rs b/ethstore/src/accounts_dir/mod.rs old mode 100755 new mode 100644 index ec72d05da32895864a92536df06150549153015e..b8dd313d2675a1e249875ee1eb7f43eef8b8701e --- a/ethstore/src/accounts_dir/mod.rs +++ b/ethstore/src/accounts_dir/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethstore/src/accounts_dir/vault.rs b/ethstore/src/accounts_dir/vault.rs old mode 100755 new mode 100644 index 1ef85402a7ac91b0ecd4e9a73d4ad435cafdd040..b2f6ce6160560c908c67069508d40637363a7f83 --- a/ethstore/src/accounts_dir/vault.rs +++ b/ethstore/src/accounts_dir/vault.rs @@ -1,4 +1,4 @@ -// Copyright 2015, 2016, 2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -235,7 +235,7 @@ fn check_vault_name(name: &str) -> bool { /// Vault can be empty, but still must be pluggable => we store vault password in separate file fn create_vault_file

(vault_dir_path: P, key: &VaultKey, meta: &str) -> Result<(), Error> where P: AsRef { let password_hash = key.password.keccak256(); - let crypto = Crypto::with_plain(&password_hash, &key.password, key.iterations); + let crypto = Crypto::with_plain(&password_hash, &key.password, key.iterations)?; let mut vault_file_path: PathBuf = vault_dir_path.as_ref().into(); vault_file_path.push(VAULT_FILE_NAME); diff --git a/ethstore/src/error.rs b/ethstore/src/error.rs old mode 100755 new mode 100644 index f7e0b0bfadc5e07fbff8a990d00199f5681082b2..6a2c257633aa9762efb8cf45204c6e6e0661089e --- a/ethstore/src/error.rs +++ b/ethstore/src/error.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -16,8 +16,8 @@ use std::fmt; use std::io::Error as IoError; -use ethkey::Error as EthKeyError; -use crypto::Error as EthCryptoError; +use ethkey::{self, Error as EthKeyError}; +use crypto::{self, Error as EthCryptoError}; use ethkey::DerivationError; /// Account-related errors. @@ -49,6 +49,8 @@ pub enum Error { CreationFailed, /// `EthKey` error EthKey(EthKeyError), + /// `ethkey::crypto::Error` + EthKeyCrypto(ethkey::crypto::Error), /// `EthCrypto` error EthCrypto(EthCryptoError), /// Derivation error @@ -73,6 +75,7 @@ impl fmt::Display for Error { Error::VaultNotFound => "Vault not found".into(), Error::CreationFailed => "Account creation failed".into(), Error::EthKey(ref err) => err.to_string(), + Error::EthKeyCrypto(ref err) => err.to_string(), Error::EthCrypto(ref err) => err.to_string(), Error::Derivation(ref err) => format!("Derivation error: {:?}", err), Error::Custom(ref s) => s.clone(), @@ -94,12 +97,30 @@ impl From for Error { } } +impl From for Error { + fn from(err: ethkey::crypto::Error) -> Self { + Error::EthKeyCrypto(err) + } +} + impl From for Error { fn from(err: EthCryptoError) -> Self { Error::EthCrypto(err) } } +impl From for Error { + fn from(err: crypto::error::ScryptError) -> Self { + Error::EthCrypto(err.into()) + } +} + +impl From for Error { + fn from(err: crypto::error::SymmError) -> Self { + Error::EthCrypto(err.into()) + } +} + impl From for Error { fn from(err: DerivationError) -> Self { Error::Derivation(err) diff --git a/ethstore/src/ethkey.rs b/ethstore/src/ethkey.rs index 48212639109f0cf535f18ed9f051c0adf582f80c..34e89a4fb5801e9fe3179abf192343d0c2388579 100644 --- a/ethstore/src/ethkey.rs +++ b/ethstore/src/ethkey.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethstore/src/ethstore.rs b/ethstore/src/ethstore.rs old mode 100755 new mode 100644 index 40a687fc9880354c8ca8bdc8208ca43662b4b7bd..13780f7f63771260b53a797a97ff1b189a215e3b --- a/ethstore/src/ethstore.rs +++ b/ethstore/src/ethstore.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -458,7 +458,7 @@ impl SimpleSecretStore for EthMultiStore { fn insert_account(&self, vault: SecretVaultRef, secret: Secret, password: &str) -> Result { let keypair = KeyPair::from_secret(secret).map_err(|_| Error::CreationFailed)?; let id: [u8; 16] = Random::random(); - let account = SafeAccount::create(&keypair, id, password, self.iterations, "".to_owned(), "{}".to_owned()); + let account = SafeAccount::create(&keypair, id, password, self.iterations, "".to_owned(), "{}".to_owned())?; self.import(vault, account) } diff --git a/ethstore/src/import.rs b/ethstore/src/import.rs index 2aaef51f504153d9808790f48e0b281137bc6b12..876119fd50a7d5aa78d9dc8819f3fb84a2a1208b 100644 --- a/ethstore/src/import.rs +++ b/ethstore/src/import.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethstore/src/json/bytes.rs b/ethstore/src/json/bytes.rs index de2c6456361979caf04132581f3f19d79acf731a..b5aae19222a78114b10575f0c085ad76d6e37e1d 100644 --- a/ethstore/src/json/bytes.rs +++ b/ethstore/src/json/bytes.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -72,4 +72,3 @@ impl From for Vec { b.0 } } - diff --git a/ethstore/src/json/cipher.rs b/ethstore/src/json/cipher.rs index 33f4ec5721155482c30530e210b63297be66e3bb..6fffdde9e2ac2d7b0427d43a6e4149b607940e3c 100644 --- a/ethstore/src/json/cipher.rs +++ b/ethstore/src/json/cipher.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethstore/src/json/crypto.rs b/ethstore/src/json/crypto.rs index 03f72e576e600c4b2a4bb250a4b0f3c8a683a326..0a926cc83fe399ad677ffe7f934936680b1ffc03 100644 --- a/ethstore/src/json/crypto.rs +++ b/ethstore/src/json/crypto.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethstore/src/json/error.rs b/ethstore/src/json/error.rs index 8a5029642dbf941fae5d9a5959cae48137f87203..81b805bfe2c82765f7853b7abeeed8a95cb7b516 100644 --- a/ethstore/src/json/error.rs +++ b/ethstore/src/json/error.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethstore/src/json/hash.rs b/ethstore/src/json/hash.rs index 13564c95d100e7211d750f9ea271b27b5dfcd049..c2ad547734f963cde00685a338029a282fa90e38 100644 --- a/ethstore/src/json/hash.rs +++ b/ethstore/src/json/hash.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethstore/src/json/id.rs b/ethstore/src/json/id.rs index aa90a4d7a495ed0ba9631740b0ecac6ff35bbf37..7df5c8f7e5afb37fa1c90acd9f1c85d0cfb808ff 100644 --- a/ethstore/src/json/id.rs +++ b/ethstore/src/json/id.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethstore/src/json/kdf.rs b/ethstore/src/json/kdf.rs index 6498323be243086368ada92a37c7f0cf5a6fae2a..f8df3c2285fedcb5d67bacb3b81b267603a7417e 100644 --- a/ethstore/src/json/kdf.rs +++ b/ethstore/src/json/kdf.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethstore/src/json/key_file.rs b/ethstore/src/json/key_file.rs index 60b34681e25f3fe4076b747e089c5e6c9c5fe3a1..2c3cf3fdd5d6953dfa57f735e1dc39c3eaa5d6b0 100644 --- a/ethstore/src/json/key_file.rs +++ b/ethstore/src/json/key_file.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -102,7 +102,6 @@ impl<'a> Deserialize<'a> for KeyFile { } } - fn none_if_empty<'a, T>(v: Option) -> Option where T: DeserializeOwned { diff --git a/ethstore/src/json/mod.rs b/ethstore/src/json/mod.rs index 865b75dea66b35dec89615ec32a6ca84fb286901..e39bff651e447921bf0ced33cdfb738ecf0eca4e 100644 --- a/ethstore/src/json/mod.rs +++ b/ethstore/src/json/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethstore/src/json/presale.rs b/ethstore/src/json/presale.rs index d1cffcb6ad948119de23e1692ec697b4e39e1e17..478f328a43af4b24901bf131e146e00d4dc64fdb 100644 --- a/ethstore/src/json/presale.rs +++ b/ethstore/src/json/presale.rs @@ -1,3 +1,19 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + use std::io::Read; use serde_json; use super::{H160, Bytes}; diff --git a/ethstore/src/json/vault_file.rs b/ethstore/src/json/vault_file.rs old mode 100755 new mode 100644 index d11e71451fb7ae533ad6ec2e4b337f68473d5bee..e9620442272147808815eafac83df1ace93fdfa2 --- a/ethstore/src/json/vault_file.rs +++ b/ethstore/src/json/vault_file.rs @@ -1,4 +1,4 @@ -// Copyright 2015, 2016, 2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethstore/src/json/vault_key_file.rs b/ethstore/src/json/vault_key_file.rs old mode 100755 new mode 100644 index 76c59b3808d22ca6c43e9bf74e15316cdf984fb0..818487d52b69e92a6546cc745004a985fdfca195 --- a/ethstore/src/json/vault_key_file.rs +++ b/ethstore/src/json/vault_key_file.rs @@ -1,4 +1,4 @@ -// Copyright 2015, 2016, 2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethstore/src/json/version.rs b/ethstore/src/json/version.rs index 0eb8450f1499fd022b1ce1df1f0b47c1bc2dd041..683d4a520fa8e440d759cd1d247bb0e075527f46 100644 --- a/ethstore/src/json/version.rs +++ b/ethstore/src/json/version.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -56,4 +56,3 @@ impl<'a> Visitor<'a> for VersionVisitor { } } } - diff --git a/ethstore/src/lib.rs b/ethstore/src/lib.rs old mode 100755 new mode 100644 index 3422a5ff72f7885a7f607854c3fca37a28abca0b..67e636dd5c9ccdbfcac3881865d2fbe86ac018d1 --- a/ethstore/src/lib.rs +++ b/ethstore/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -18,7 +18,6 @@ #![warn(missing_docs)] -extern crate crypto as rcrypto; extern crate dir; extern crate itertools; extern crate libc; @@ -28,12 +27,11 @@ extern crate rustc_hex; extern crate serde; extern crate serde_json; extern crate smallvec; -extern crate subtle; extern crate time; extern crate tiny_keccak; extern crate tempdir; -extern crate ethcrypto as crypto; +extern crate ethcore_crypto as crypto; extern crate ethereum_types; extern crate ethkey as _ethkey; extern crate parity_wordlist; diff --git a/ethstore/src/presale.rs b/ethstore/src/presale.rs index 0c3e72e31b805f792bcc1a40242520c7fa53662d..d7b80d240cc528ecec01400814b1cc6d68d71b45 100644 --- a/ethstore/src/presale.rs +++ b/ethstore/src/presale.rs @@ -1,11 +1,24 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + use std::fs; use std::path::Path; -use rcrypto::pbkdf2::pbkdf2; -use rcrypto::sha2::Sha256; -use rcrypto::hmac::Hmac; use json; use ethkey::{Address, Secret, KeyPair}; -use crypto::Keccak256; +use crypto::{Keccak256, pbkdf2}; use {crypto, Error}; /// Pre-sale wallet. @@ -42,12 +55,14 @@ impl PresaleWallet { /// Decrypt the wallet. pub fn decrypt(&self, password: &str) -> Result { - let mut h_mac = Hmac::new(Sha256::new(), password.as_bytes()); - let mut derived_key = vec![0u8; 16]; - pbkdf2(&mut h_mac, password.as_bytes(), 2000, &mut derived_key); + 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_cbc(&derived_key, &self.iv, &self.ciphertext, &mut key).map_err(|_| Error::InvalidPassword)?; + let len = crypto::aes::decrypt_128_cbc(&derived_key[0..16], &self.iv, &self.ciphertext, &mut key) + .map_err(|_| Error::InvalidPassword)?; let unpadded = &key[..len]; let secret = Secret::from_unsafe_slice(&unpadded.keccak256())?; diff --git a/ethstore/src/random.rs b/ethstore/src/random.rs index af754471e1d5bdd85d356e4c4015f03c43ffe0e7..b8b7a71fa8b4e5637af80124def236be6607528a 100644 --- a/ethstore/src/random.rs +++ b/ethstore/src/random.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -43,4 +43,3 @@ pub fn random_string(length: usize) -> String { let mut rng = OsRng::new().expect("Not able to operate without random source."); rng.gen_ascii_chars().take(length).collect() } - diff --git a/ethstore/src/secret_store.rs b/ethstore/src/secret_store.rs old mode 100755 new mode 100644 index ebac2f992292cb0e79dfe6b1932aded84f752578..fd37267a7472ed4a202ad2ad90c10af5a8fff1cd --- a/ethstore/src/secret_store.rs +++ b/ethstore/src/secret_store.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethstore/tests/api.rs b/ethstore/tests/api.rs old mode 100755 new mode 100644 index fb24ff3367d73661ffad6718f15c7784dd2b060e..5e4eaab817ab82063b6beb5462cfcd11627fa168 --- a/ethstore/tests/api.rs +++ b/ethstore/tests/api.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethstore/tests/util/mod.rs b/ethstore/tests/util/mod.rs index c0002d4e13d090159ec0fb84e60dafee65214b7e..1a7abc93effbfb1422019b568d6958866ed53bdf 100644 --- a/ethstore/tests/util/mod.rs +++ b/ethstore/tests/util/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ethstore/tests/util/transient_dir.rs b/ethstore/tests/util/transient_dir.rs old mode 100755 new mode 100644 index dcc65ec6985365f918fc4aa4d2c5ecb8261cb4f9..c0969418d8c7155a4f93ef2cf1c31b0603dfc9fe --- a/ethstore/tests/util/transient_dir.rs +++ b/ethstore/tests/util/transient_dir.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/evmbin/Cargo.toml b/evmbin/Cargo.toml index 000019214a7c789efc8c8fb165e7939e3c57730e..51865091a905fdd9c724188d518ada205f63805d 100644 --- a/evmbin/Cargo.toml +++ b/evmbin/Cargo.toml @@ -10,11 +10,11 @@ path = "./src/main.rs" [dependencies] docopt = "0.8" -ethcore = { path = "../ethcore" } +ethcore = { path = "../ethcore", features = ["test-helpers"] } ethjson = { path = "../json" } ethcore-bytes = { path = "../util/bytes" } ethcore-transaction = { path = "../ethcore/transaction" } -ethereum-types = "0.2" +ethereum-types = "0.3" evm = { path = "../ethcore/evm" } panic_hook = { path = "../util/panic_hook" } rustc-hex = "1.0" diff --git a/evmbin/benches/mod.rs b/evmbin/benches/mod.rs index 6b6746e747b25354c3cbcf2cd6b35b3b4a697c2a..8fdd5e9cfe13e5e76f1c893460f3c66844f7b337 100644 --- a/evmbin/benches/mod.rs +++ b/evmbin/benches/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -83,4 +83,3 @@ fn rng(gas: U256, b: &mut Bencher) { run_vm(params) }); } - diff --git a/evmbin/src/display/json.rs b/evmbin/src/display/json.rs index 00ca91b94fef22d9bf7de73257877f4386921969..ccee9c371702b8987a92bf706dd987e4fed3aa2c 100644 --- a/evmbin/src/display/json.rs +++ b/evmbin/src/display/json.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -156,7 +156,6 @@ impl trace::VMTracer for Informant { self.storage.insert(pos.into(), val.into()); } - if !self.subtraces.is_empty() { self.traces.extend(mem::replace(&mut self.subtraces, vec![])); } diff --git a/evmbin/src/display/mod.rs b/evmbin/src/display/mod.rs index b9390058b6022230c745786f9cdcb32caa0dac50..a8eb20d9e6c86986189b08534e09eff166c2fa2e 100644 --- a/evmbin/src/display/mod.rs +++ b/evmbin/src/display/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/evmbin/src/display/simple.rs b/evmbin/src/display/simple.rs index 30bb8ffcf7a286c8a6f557f3fd04efbb95764b26..8ff863cfa944f7d19dc56be40783a325b47ebfd6 100644 --- a/evmbin/src/display/simple.rs +++ b/evmbin/src/display/simple.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/evmbin/src/display/std_json.rs b/evmbin/src/display/std_json.rs index 3d8f52dbd1f744068788aaca69d76b6e4313b0e7..6c4dac1626a12e27a060ceeca14b8e5c4d4910b6 100644 --- a/evmbin/src/display/std_json.rs +++ b/evmbin/src/display/std_json.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/evmbin/src/info.rs b/evmbin/src/info.rs index 1be81d9132f9d1d80af1f42cdf46dfe77aea7cc2..d1cd3cf6fc9f2208212bd2dba8ba5fe1e954d476 100644 --- a/evmbin/src/info.rs +++ b/evmbin/src/info.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/evmbin/src/main.rs b/evmbin/src/main.rs index bddb40ecd235c38cabe0572385381d8e69a5a964..4620143f7674b699eb79b98e914a662c35b3901c 100644 --- a/evmbin/src/main.rs +++ b/evmbin/src/main.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -83,7 +83,6 @@ General options: -h, --help Display this message and exit. "#; - fn main() { panic_hook::set(); diff --git a/evmjit/Cargo.toml b/evmjit/Cargo.toml deleted file mode 100644 index c7bf1f8ba9408e56cb753dddce3f8ae9334a8194..0000000000000000000000000000000000000000 --- a/evmjit/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "evmjit" -version = "1.11.0" -authors = ["Parity Technologies "] - -[lib] -crate-type = ["dylib"] - -[dependencies] -tiny-keccak = "1.3" diff --git a/evmjit/src/lib.rs b/evmjit/src/lib.rs deleted file mode 100644 index 06aade3c2ff1b9f7d57c7a7b42100eb40c5a2d54..0000000000000000000000000000000000000000 --- a/evmjit/src/lib.rs +++ /dev/null @@ -1,493 +0,0 @@ -// Copyright 2015-2017 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 . - -//! Bare rust wrapper around evmjit. -//! -//! Requires latest version of Ethereum EVM JIT. https://github.com/debris/evmjit -//! -//! ``` -//! extern crate evmjit; -//! use evmjit::*; -//! -//! fn main() { -//! let mut context = ContextHandle::new(RuntimeDataHandle::new(), ExtHandle::empty()); -//! assert_eq!(context.exec(), ReturnCode::Stop); -//! } -//! ``` -//! -//! -//! To verify that c abi is "imported" correctly, run: -//! -//! ```bash -//! nm your_executable -g | grep ext -//! ``` -//! -//! It should give the following output: -//! -//! ```bash -//! 00000001000779e0 T _ext_balance -//! 0000000100077a10 T _ext_blockhash -//! 0000000100077a90 T _ext_call -//! 0000000100077a40 T _ext_create -//! 0000000100077b50 T _ext_extcode -//! 0000000100077b80 T _ext_log -//! 0000000100077b20 T _ext_sha3 -//! 0000000100077980 T _ext_sload -//! 00000001000779b0 T _ext_sstore -//! ``` - -extern crate tiny_keccak; - -use std::ops::{Deref, DerefMut}; -use self::ffi::*; - -pub use self::ffi::JitReturnCode as ReturnCode; -pub use self::ffi::JitI256 as I256; -pub use self::ffi::JitH256 as H256; - -/// Takes care of proper initialization and destruction of `RuntimeData`. -/// -/// This handle must be used to create runtime data, -/// cause underneath it's a `C++` structure. Incompatible with rust -/// structs. -pub struct RuntimeDataHandle { - runtime_data: *mut JitRuntimeData -} - -impl RuntimeDataHandle { - /// Creates new `RuntimeData` handle. - pub fn new() -> Self { - RuntimeDataHandle { - runtime_data: unsafe { evmjit_create_runtime_data() } - } - } -} - -impl Drop for RuntimeDataHandle { - fn drop(&mut self) { - unsafe { evmjit_destroy_runtime_data(self.runtime_data) } - } -} - -impl Deref for RuntimeDataHandle { - type Target = JitRuntimeData; - - fn deref(&self) -> &Self::Target { - unsafe { &*self.runtime_data } - } -} - -impl DerefMut for RuntimeDataHandle { - fn deref_mut(&mut self) -> &mut Self::Target { - unsafe { &mut *self.runtime_data } - } -} - -/// Takes care of proper initilization and destruction of `JitSchedule`. -/// -/// This handle must be used to jit schedule, -/// cause underneath it's a `C++` structure. Incompatible with rust -/// structs. -pub struct ScheduleHandle { - schedule: *mut JitSchedule -} - -impl ScheduleHandle { - /// Creates new `Schedule` handle. - pub fn new() -> Self { - ScheduleHandle { - schedule: unsafe { evmjit_create_schedule() } - } - } -} - -impl Drop for ScheduleHandle { - fn drop(&mut self) { - unsafe { evmjit_destroy_schedule(self.schedule) } - } -} - -impl Deref for ScheduleHandle { - type Target = JitSchedule; - - fn deref(&self) -> &Self::Target { - unsafe { &*self.schedule } - } -} - -impl DerefMut for ScheduleHandle { - fn deref_mut(&mut self) -> &mut Self::Target { - unsafe { &mut *self.schedule } - } -} - -/// Takes care of proper initialization and destruction of jit context. -/// -/// This handle must be used to create context, -/// cause underneath it's a `C++` structure. Incombatible with rust -/// structs. -pub struct ContextHandle { - context: *mut JitContext, - data_handle: RuntimeDataHandle, - schedule_handle: ScheduleHandle -} - -impl ContextHandle { - /// Creates new context handle. - /// - /// This function is unsafe cause ext lifetime is not considered - /// We also can't make ExtHandle a member of `ContextHandle` structure, - /// cause this would be a move operation or it would require a template - /// lifetime to a reference. Both solutions are not possible. - pub unsafe fn new(data_handle: RuntimeDataHandle, schedule_handle: ScheduleHandle, ext: &mut ExtHandle) -> Self { - let mut handle = ContextHandle { - context: std::mem::uninitialized(), - schedule_handle: schedule_handle, - data_handle: data_handle, - }; - - handle.context = evmjit_create_context(handle.data_handle.deref_mut(), ext); - handle - } - - /// Executes context. - pub fn exec(&mut self) -> JitReturnCode { - unsafe { evmjit_exec(self.context, self.schedule_handle.deref_mut()) } - } - - /// Returns output data. - pub fn output_data(&self) -> &[u8] { - unsafe { std::slice::from_raw_parts(self.data_handle.call_data, self.data_handle.call_data_size as usize) } - } - - /// Returns address to which funds should be transfered after suicide. - pub fn suicide_refund_address(&self) -> JitI256 { - // evmjit reuses data_handle address field to store suicide address - self.data_handle.address - } - - /// Returns gas left. - pub fn gas_left(&self) -> u64 { - self.data_handle.gas as u64 - } -} - -impl Drop for ContextHandle { - fn drop(&mut self) { - unsafe { evmjit_destroy_context(self.context); } - } -} - -/// Component oriented wrapper around jit ext c interface. -pub trait Ext { - fn sload(&self, index: *const JitI256, out_value: *mut JitI256); - fn sstore(&mut self, index: *const JitI256, value: *const JitI256); - fn balance(&self, address: *const JitH256, out_value: *mut JitI256); - fn blockhash(&self, number: *const JitI256, out_hash: *mut JitH256); - - fn create(&mut self, - io_gas: *mut u64, - endowment: *const JitI256, - init_beg: *const u8, - init_size: u64, - address: *mut JitH256); - - fn call(&mut self, - io_gas: *mut u64, - call_gas: u64, - sender_address: *const JitH256, - receive_address: *const JitH256, - code_address: *const JitH256, - transfer_value: *const JitI256, - apparent_value: *const JitI256, - in_beg: *const u8, - in_size: u64, - out_beg: *mut u8, - out_size: u64) -> bool; - - fn log(&mut self, - beg: *const u8, - size: u64, - topic1: *const JitH256, - topic2: *const JitH256, - topic3: *const JitH256, - topic4: *const JitH256); - - fn extcode(&self, address: *const JitH256, size: *mut u64) -> *const u8; -} - -/// C abi compatible wrapper for jit ext implementers. -pub struct ExtHandle { - ext_impl: Option> -} - -impl ExtHandle { - /// Creates new extironment wrapper for given implementation - pub fn new(ext_impl: T) -> Self where T: Ext + 'static { - ExtHandle { ext_impl: Some(Box::new(ext_impl)) } - } - - /// Creates empty extironment. - /// It can be used to for any operations. - pub fn empty() -> Self { - ExtHandle { ext_impl: None } - } -} - -impl Deref for ExtHandle { - type Target = Box; - - fn deref(&self) -> &Self::Target { - match self.ext_impl { - Some(ref ext) => ext, - None => { panic!("Handle is empty!"); } - } - } -} - -impl DerefMut for ExtHandle { - fn deref_mut(&mut self) -> &mut Self::Target { - match self.ext_impl { - Some(ref mut ext) => ext, - None => { panic!("Handle is empty!"); } - } - } -} - -/// ffi functions -pub mod ffi { - use std::slice; - use std::mem; - use tiny_keccak::Keccak; - use super::*; - - /// Jit context struct declaration. - pub enum JitContext {} - - #[repr(C)] - #[derive(Debug, Eq, PartialEq)] - /// Jit context execution return code. - pub enum JitReturnCode { - Stop = 0, - Return = 1, - Suicide = 2, - - OutOfGas = -1, - - LLVMError = -101, - UnexpectedError = -111 - } - - #[repr(C)] - #[derive(Debug, Copy, Clone)] - /// Signed 256 bit integer. - pub struct JitI256 { - pub words: [u64; 4] - } - - #[repr(C)] - #[derive(Debug, Copy, Clone)] - /// Jit Hash - pub struct JitH256 { - pub words: [u64; 4] - } - - impl From for JitI256 { - fn from(mut hash: JitH256) -> JitI256 { - unsafe { - { - let bytes: &mut [u8] = slice::from_raw_parts_mut(hash.words.as_mut_ptr() as *mut u8, 32); - bytes.reverse(); - } - mem::transmute(hash) - } - } - } - - impl From for JitH256 { - fn from(mut i: JitI256) -> JitH256 { - unsafe { - { - let bytes: &mut [u8] = slice::from_raw_parts_mut(i.words.as_mut_ptr() as *mut u8, 32); - bytes.reverse(); - } - mem::transmute(i) - } - } - } - - #[repr(C)] - #[derive(Debug)] - /// Jit runtime data. - pub struct JitRuntimeData { - pub gas: i64, - pub gas_price: i64, - pub call_data: *const u8, - pub call_data_size: u64, - pub address: JitI256, - pub caller: JitI256, - pub origin: JitI256, - pub transfer_value: JitI256, - pub apparent_value: JitI256, - pub author: JitI256, - pub difficulty: JitI256, - pub gas_limit: JitI256, - pub number: u64, - pub timestamp: i64, - pub code: *const u8, - pub code_size: u64, - pub code_hash: JitI256 - } - - #[repr(C)] - #[derive(Debug)] - /// Configurable properties of git schedule. - pub struct JitSchedule { - pub have_delegate_call: bool - } - - #[no_mangle] - pub unsafe extern "C" fn env_sload(ext: *const ExtHandle, index: *const JitI256, out_value: *mut JitI256) { - let ext = &*ext; - ext.sload(index, out_value); - } - - #[no_mangle] - pub unsafe extern "C" fn env_sstore(ext: *mut ExtHandle, index: *mut JitI256, value: *mut JitI256) { - let ext = &mut *ext; - ext.sstore(index, value); - } - - #[no_mangle] - pub unsafe extern "C" fn env_balance(ext: *const ExtHandle, address: *const JitH256, out_value: *mut JitI256) { - let ext = &*ext; - ext.balance(address, out_value); - } - - #[no_mangle] - pub unsafe extern "C" fn env_blockhash(ext: *const ExtHandle, number: *const JitI256, out_hash: *mut JitH256) { - let ext = &*ext; - ext.blockhash(number, out_hash); - } - - #[no_mangle] - pub unsafe extern "C" fn env_create(ext: *mut ExtHandle, - io_gas: *mut u64, - endowment: *const JitI256, - init_beg: *const u8, - init_size: u64, - address: *mut JitH256) { - let ext = &mut *ext; - ext.create(io_gas, endowment, init_beg, init_size, address); - } - - #[no_mangle] - pub unsafe extern "C" fn env_call(ext: *mut ExtHandle, - io_gas: *mut u64, - call_gas: u64, - sender_address: *const JitH256, - receive_address: *const JitH256, - code_address: *const JitH256, - transfer_value: *const JitI256, - apparent_value: *const JitI256, - in_beg: *const u8, - in_size: u64, - out_beg: *mut u8, - out_size: u64) -> bool { - let ext = &mut *ext; - ext.call(io_gas, call_gas, sender_address, receive_address, code_address, transfer_value, apparent_value, in_beg, in_size, out_beg, out_size) - } - - #[no_mangle] - pub unsafe extern "C" fn env_sha3(begin: *const u8, size: u64, out_hash: *mut JitH256) { - let out_hash = &mut *out_hash; - let input = slice::from_raw_parts(begin, size as usize); - let outlen = out_hash.words.len() * 8; - let output = slice::from_raw_parts_mut(out_hash.words.as_mut_ptr() as *mut u8, outlen); - let mut sha3 = Keccak::new_keccak256(); - sha3.update(input); - sha3.finalize(output); - } - - #[no_mangle] - pub unsafe extern "C" fn env_extcode(ext: *const ExtHandle, address: *const JitH256, size: *mut u64) -> *const u8 { - let ext = &*ext; - ext.extcode(address, size) - } - - #[no_mangle] - pub unsafe extern "C" fn env_log(ext: *mut ExtHandle, - beg: *const u8, - size: u64, - topic1: *const JitH256, - topic2: *const JitH256, - topic3: *const JitH256, - topic4: *const JitH256) { - let ext = &mut *ext; - ext.log(beg, size, topic1, topic2, topic3, topic4); - } - - - #[link(name="evmjit")] - extern "C" { - pub fn evmjit_create_schedule() -> *mut JitSchedule; - pub fn evmjit_destroy_schedule(schedule: *mut JitSchedule); - pub fn evmjit_create_runtime_data() -> *mut JitRuntimeData; - pub fn evmjit_destroy_runtime_data(data: *mut JitRuntimeData); - pub fn evmjit_destroy_context(context: *mut JitContext); - pub fn evmjit_exec(context: *mut JitContext, schedule: *mut JitSchedule) -> JitReturnCode; - } - - // ExtHandle is not a C type, so we need to allow "improper_ctypes" - #[link(name="evmjit")] - #[allow(improper_ctypes)] - extern "C" { - pub fn evmjit_create_context(data: *mut JitRuntimeData, ext: *mut ExtHandle) -> *mut JitContext; - } -} - -#[test] -fn ffi_test() { - unsafe { - let data = evmjit_create_runtime_data(); - let schedule = evmjit_create_schedule(); - let context = evmjit_create_context(data, &mut ExtHandle::empty()); - - let code = evmjit_exec(context, schedule); - assert_eq!(code, JitReturnCode::Stop); - - evmjit_destroy_schedule(schedule); - evmjit_destroy_runtime_data(data); - evmjit_destroy_context(context); - } -} - -#[test] -fn handle_test() { - unsafe { - let mut ext = ExtHandle::empty(); - let mut context = ContextHandle::new(RuntimeDataHandle::new(), ScheduleHandle::new(), &mut ext); - assert_eq!(context.exec(), ReturnCode::Stop); - } -} - -#[test] -fn hash_to_int() { - let h = H256 { words:[0x0123456789abcdef, 0, 0, 0] }; - let i = I256::from(h); - assert_eq!([0u64, 0, 0, 0xefcdab8967452301], i.words); - assert_eq!(H256::from(i).words, h.words); -} diff --git a/hash-fetch/Cargo.toml b/hash-fetch/Cargo.toml index d5630993670ab6e15260c9173d10d5317c6f58e9..d4f46a121e89327057da587e6c8f67b214788071 100644 --- a/hash-fetch/Cargo.toml +++ b/hash-fetch/Cargo.toml @@ -3,7 +3,7 @@ description = "Fetching hash-addressed content." homepage = "http://parity.io" license = "GPL-3.0" name = "parity-hash-fetch" -version = "1.11.0" +version = "1.12.0" authors = ["Parity Technologies "] [dependencies] @@ -16,7 +16,7 @@ rand = "0.4" rustc-hex = "1.0" fetch = { path = "../util/fetch" } ethcore-bytes = { path = "../util/bytes" } -ethereum-types = "0.2" +ethereum-types = "0.3" parity-reactor = { path = "../util/reactor" } keccak-hash = { path = "../util/hash" } registrar = { path = "../registrar" } @@ -28,3 +28,4 @@ ethabi-contract = "5.0" [dev-dependencies] hyper = "0.11" parking_lot = "0.5" +fake-fetch = { path = "../util/fake-fetch" } diff --git a/hash-fetch/src/client.rs b/hash-fetch/src/client.rs index b3a9356ea2c5b57ee85c728fbaa6d908d1efdddf..ebdab681a4fe83502b3839cb0d3407fe026e443f 100644 --- a/hash-fetch/src/client.rs +++ b/hash-fetch/src/client.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -153,7 +153,7 @@ impl HashFetch for Client { .into_future() .and_then(move |url| { debug!(target: "fetch", "Resolved {:?} to {:?}. Fetching...", hash, url); - remote_fetch.fetch(&url, abort).from_err() + remote_fetch.get(&url, abort).from_err() }) .and_then(move |response| { if !response.is_success() { @@ -193,37 +193,14 @@ fn random_temp_path() -> PathBuf { #[cfg(test)] mod tests { - extern crate hyper; + use fake_fetch::FakeFetch; use rustc_hex::FromHex; use std::sync::{Arc, mpsc}; use parking_lot::Mutex; - use futures::future; use futures_cpupool::CpuPool; - use fetch::{self, Fetch, Url}; use parity_reactor::Remote; use urlhint::tests::{FakeRegistrar, URLHINT}; use super::{Error, Client, HashFetch, random_temp_path}; - use self::hyper::StatusCode; - - - #[derive(Clone)] - struct FakeFetch { - return_success: bool - } - - impl Fetch for FakeFetch { - type Result = future::Ok; - - fn fetch(&self, url: &str, abort: fetch::Abort) -> Self::Result { - assert_eq!(url, "https://parity.io/assets/images/ethcore-black-horizontal.png"); - let u = Url::parse(url).unwrap(); - future::ok(if self.return_success { - fetch::client::Response::new(u, hyper::Response::new().with_body(&b"result"[..]), abort) - } else { - fetch::client::Response::new(u, hyper::Response::new().with_status(StatusCode::NotFound), abort) - }) - } - } fn registrar() -> FakeRegistrar { let mut registrar = FakeRegistrar::new(); @@ -238,7 +215,7 @@ mod tests { fn should_return_error_if_hash_not_found() { // given let contract = Arc::new(FakeRegistrar::new()); - let fetch = FakeFetch { return_success: false }; + let fetch = FakeFetch::new(None::); let client = Client::with_fetch(contract.clone(), CpuPool::new(1), fetch, Remote::new_sync()); // when @@ -256,7 +233,7 @@ mod tests { fn should_return_error_if_response_is_not_successful() { // given let registrar = Arc::new(registrar()); - let fetch = FakeFetch { return_success: false }; + let fetch = FakeFetch::new(None::); let client = Client::with_fetch(registrar.clone(), CpuPool::new(1), fetch, Remote::new_sync()); // when @@ -274,7 +251,7 @@ mod tests { fn should_return_hash_mismatch() { // given let registrar = Arc::new(registrar()); - let fetch = FakeFetch { return_success: true }; + let fetch = FakeFetch::new(Some(1)); let mut client = Client::with_fetch(registrar.clone(), CpuPool::new(1), fetch, Remote::new_sync()); let path = random_temp_path(); let path2 = path.clone(); @@ -288,7 +265,7 @@ mod tests { // then let result = rx.recv().unwrap(); - let hash = "0x06b0a4f426f6713234b2d4b2468640bc4e0bb72657a920ad24c5087153c593c8".into(); + let hash = "0x2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e".into(); assert_eq!(result.unwrap_err(), Error::HashMismatch { expected: 2.into(), got: hash }); assert!(!path.exists(), "Temporary file should be removed."); } @@ -297,12 +274,12 @@ mod tests { fn should_return_path_if_hash_matches() { // given let registrar = Arc::new(registrar()); - let fetch = FakeFetch { return_success: true }; + let fetch = FakeFetch::new(Some(1)); let client = Client::with_fetch(registrar.clone(), CpuPool::new(1), fetch, Remote::new_sync()); // when let (tx, rx) = mpsc::channel(); - client.fetch("0x06b0a4f426f6713234b2d4b2468640bc4e0bb72657a920ad24c5087153c593c8".into(), + client.fetch("0x2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e".into(), Default::default(), Box::new(move |result| { tx.send(result).unwrap(); })); @@ -311,4 +288,3 @@ mod tests { assert!(result.is_ok(), "Should return path, got: {:?}", result); } } - diff --git a/hash-fetch/src/lib.rs b/hash-fetch/src/lib.rs index 6e74982bb844ca32282f5e5be2adf40d3499a76e..bdbb0e350544403ba2443677a951c7b491acac94 100644 --- a/hash-fetch/src/lib.rs +++ b/hash-fetch/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -42,6 +42,10 @@ extern crate ethabi_derive; extern crate ethabi_contract; #[cfg(test)] extern crate parking_lot; +#[cfg(test)] +extern crate hyper; +#[cfg(test)] +extern crate fake_fetch; mod client; diff --git a/hash-fetch/src/urlhint.rs b/hash-fetch/src/urlhint.rs index d05dd40a22f225633ed574ea12c1c2886e06d806..d80566ea6202f8077b9bdedb7e316225b3e69810 100644 --- a/hash-fetch/src/urlhint.rs +++ b/hash-fetch/src/urlhint.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -265,8 +265,6 @@ pub mod tests { let calls = registrar.calls.clone(); let urlhint = URLHintContract::new(Arc::new(registrar)); - - // when let res = urlhint.resolve("test".as_bytes().into()).wait().unwrap(); let calls = calls.lock(); @@ -353,7 +351,6 @@ pub mod tests { let url4 = "https://parity.io/parity.png#content-type=image/jpeg"; let url5 = "https://parity.io/parity.png"; - assert_eq!(guess_mime_type(url1), None); assert_eq!(guess_mime_type(url2), Some(mime::IMAGE_PNG)); assert_eq!(guess_mime_type(url3), Some(mime::IMAGE_PNG)); diff --git a/hw/Cargo.toml b/hw/Cargo.toml index c799c79c3820edeb06485ce4205438a41eacb40e..dd4adcefd5c31ecc1d20dc25e0b6f145d34b0691 100644 --- a/hw/Cargo.toml +++ b/hw/Cargo.toml @@ -3,7 +3,7 @@ description = "Hardware wallet support." homepage = "http://parity.io" license = "GPL-3.0" name = "hardware-wallet" -version = "1.11.0" +version = "1.12.0" authors = ["Parity Technologies "] [dependencies] @@ -14,7 +14,8 @@ hidapi = { git = "https://github.com/paritytech/hidapi-rs" } libusb = { git = "https://github.com/paritytech/libusb-rs" } trezor-sys = { git = "https://github.com/paritytech/trezor-sys" } ethkey = { path = "../ethkey" } -ethereum-types = "0.2" +ethereum-types = "0.3" +semver = "0.9" [dev-dependencies] rustc-hex = "1.0" diff --git a/hw/src/ledger.rs b/hw/src/ledger.rs index e7d616d4f9fcc473087e621458f0b94611360fac..f9e20654839ac93d915ad7d928f0e3f26391b2f2 100644 --- a/hw/src/ledger.rs +++ b/hw/src/ledger.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -15,32 +15,38 @@ // along with Parity. If not, see . //! Ledger hardware wallet module. Supports Ledger Blue and Nano S. -/// See https://github.com/LedgerHQ/blue-app-eth/blob/master/doc/ethapp.asc for protocol details. +//! See for protocol details. use std::cmp::min; -use std::fmt; use std::str::FromStr; -use std::sync::{Arc, Weak}; +use std::sync::{atomic, atomic::AtomicBool, Arc, Weak}; use std::time::{Duration, Instant}; +use std::{fmt, thread}; use ethereum_types::{H256, Address}; use ethkey::Signature; use hidapi; use libusb; use parking_lot::{Mutex, RwLock}; +use semver::Version as FirmwareVersion; +use super::{WalletInfo, KeyPath, Device, DeviceDirection, Wallet, USB_DEVICE_CLASS_DEVICE, POLLING_DURATION}; -use super::{WalletInfo, KeyPath}; - -/// Ledger vendor ID -pub const LEDGER_VID: u16 = 0x2c97; -/// Legder product IDs: [Nano S and Blue] -pub const LEDGER_PIDS: [u16; 2] = [0x0000, 0x0001]; +const APDU_TAG: u8 = 0x05; +const APDU_CLA: u8 = 0xe0; +const APDU_PAYLOAD_HEADER_LEN: usize = 7; const ETH_DERIVATION_PATH_BE: [u8; 17] = [4, 0x80, 0, 0, 44, 0x80, 0, 0, 60, 0x80, 0, 0, 0, 0, 0, 0, 0]; // 44'/60'/0'/0 const ETC_DERIVATION_PATH_BE: [u8; 21] = [5, 0x80, 0, 0, 44, 0x80, 0, 0, 60, 0x80, 0x02, 0x73, 0xd0, 0x80, 0, 0, 0, 0, 0, 0, 0]; // 44'/60'/160720'/0'/0 -const APDU_TAG: u8 = 0x05; -const APDU_CLA: u8 = 0xe0; +/// Ledger vendor ID +const LEDGER_VID: u16 = 0x2c97; +/// Ledger product IDs: [Nano S and Blue] +const LEDGER_PIDS: [u16; 2] = [0x0000, 0x0001]; +const LEDGER_TRANSPORT_HEADER_LEN: usize = 5; + +const MAX_CHUNK_SIZE: usize = 255; + +const HID_PACKET_SIZE: usize = 64 + HID_PREFIX_ZERO; #[cfg(windows)] const HID_PREFIX_ZERO: usize = 1; #[cfg(not(windows))] const HID_PREFIX_ZERO: usize = 0; @@ -49,6 +55,7 @@ mod commands { pub const GET_APP_CONFIGURATION: u8 = 0x06; pub const GET_ETH_PUBLIC_ADDRESS: u8 = 0x02; pub const SIGN_ETH_TRANSACTION: u8 = 0x04; + pub const SIGN_ETH_PERSONAL_MESSAGE: u8 = 0x08; } /// Hardware wallet error. @@ -64,8 +71,14 @@ pub enum Error { KeyNotFound, /// Signing has been cancelled by user. UserCancel, - /// Invalid Device + /// Invalid device InvalidDevice, + /// Impossible error + Impossible, + /// No device arrived + NoDeviceArrived, + /// No device left + NoDeviceLeft, } impl fmt::Display for Error { @@ -77,223 +90,145 @@ impl fmt::Display for Error { Error::KeyNotFound => write!(f, "Key not found"), Error::UserCancel => write!(f, "Operation has been cancelled"), Error::InvalidDevice => write!(f, "Unsupported product was entered"), + Error::Impossible => write!(f, "Placeholder error"), + Error::NoDeviceArrived => write!(f, "No device arrived"), + Error::NoDeviceLeft=> write!(f, "No device left"), } } } impl From for Error { - fn from(err: hidapi::HidError) -> Error { + fn from(err: hidapi::HidError) -> Self { Error::Usb(err) } } impl From for Error { - fn from(err: libusb::Error) -> Error { + fn from(err: libusb::Error) -> Self { Error::LibUsb(err) } } /// Ledger device manager. -pub struct Manager { +pub (crate) struct Manager { usb: Arc>, devices: RwLock>, key_path: RwLock, } -#[derive(Debug)] -struct Device { - path: String, - info: WalletInfo, -} - impl Manager { /// Create a new instance. - pub fn new(hidapi: Arc>) -> Manager { - Manager { + pub fn new(hidapi: Arc>, exiting: Arc) -> Result, libusb::Error> { + let manager = Arc::new(Self { usb: hidapi, devices: RwLock::new(Vec::new()), key_path: RwLock::new(KeyPath::Ethereum), - } - } - - /// Re-populate device list. Only those devices that have Ethereum app open will be added. - pub fn update_devices(&self) -> Result { - let mut usb = self.usb.lock(); - usb.refresh_devices(); - let devices = usb.devices(); - let mut new_devices = Vec::new(); - let mut num_new_devices = 0; - for device in devices { - trace!("Checking device: {:?}", device); - if device.vendor_id != LEDGER_VID || !LEDGER_PIDS.contains(&device.product_id) { - continue; - } - match self.read_device_info(&usb, &device) { - Ok(info) => { - debug!("Found device: {:?}", info); - if !self.devices.read().iter().any(|d| d.path == info.path) { - num_new_devices += 1; + }); + + let usb_context = Arc::new(libusb::Context::new()?); + let m = manager.clone(); + + // Subscribe to all Ledger devices + // This means that we need to check that the given productID is supported + // None => LIBUSB_HOTPLUG_MATCH_ANY, in other words that all are subscribed to + // More info can be found: + usb_context.register_callback( + Some(LEDGER_VID), None, Some(USB_DEVICE_CLASS_DEVICE), + Box::new(EventHandler::new(Arc::downgrade(&manager)))).expect("usb_callback"); + + // Ledger event handler thread + thread::Builder::new() + .spawn(move || { + if let Err(e) = m.update_devices(DeviceDirection::Arrived) { + debug!(target: "hw", "Ledger couldn't connect at startup, error: {}", e); + } + loop { + usb_context.handle_events(Some(Duration::from_millis(500))) + .unwrap_or_else(|e| debug!(target: "hw", "Ledger event handler error: {}", e)); + if exiting.load(atomic::Ordering::Acquire) { + break; } - new_devices.push(info); - } - Err(e) => debug!("Error reading device info: {}", e), - }; - } - *self.devices.write() = new_devices; - Ok(num_new_devices) - } - - /// Select key derivation path for a known chain. - pub fn set_key_path(&self, key_path: KeyPath) { - *self.key_path.write() = key_path; - } - - fn read_device_info(&self, usb: &hidapi::HidApi, dev_info: &hidapi::HidDeviceInfo) -> Result { - let mut handle = self.open_path(|| usb.open_path(&dev_info.path))?; - let address = Self::read_wallet_address(&mut handle, *self.key_path.read())?; - let manufacturer = dev_info.manufacturer_string.clone().unwrap_or("Unknown".to_owned()); - let name = dev_info.product_string.clone().unwrap_or("Unknown".to_owned()); - let serial = dev_info.serial_number.clone().unwrap_or("Unknown".to_owned()); - Ok(Device { - path: dev_info.path.clone(), - info: WalletInfo { - name: name, - manufacturer: manufacturer, - serial: serial, - address: address, - }, - }) - } - - fn read_wallet_address(handle: &hidapi::HidDevice, key_path: KeyPath) -> Result { - let ver = Self::send_apdu(handle, commands::GET_APP_CONFIGURATION, 0, 0, &[])?; - if ver.len() != 4 { - return Err(Error::Protocol("Version packet size mismatch")); - } - - let (major, minor, patch) = (ver[1], ver[2], ver[3]); - if major < 1 || (major == 1 && minor == 0 && patch < 3) { - return Err(Error::Protocol("App version 1.0.3 is required.")); - } - - let eth_path = Ð_DERIVATION_PATH_BE[..]; - let etc_path = &ETC_DERIVATION_PATH_BE[..]; - let derivation_path = match key_path { - KeyPath::Ethereum => eth_path, - KeyPath::EthereumClassic => etc_path, - }; - let key_and_address = Self::send_apdu(handle, commands::GET_ETH_PUBLIC_ADDRESS, 0, 0, derivation_path)?; - if key_and_address.len() != 107 { // 1 + 65 PK + 1 + 40 Addr (ascii-hex) - return Err(Error::Protocol("Key packet size mismatch")); - } - let address_string = ::std::str::from_utf8(&key_and_address[67..107]) - .map_err(|_| Error::Protocol("Invalid address string"))?; - - let address = Address::from_str(&address_string) - .map_err(|_| Error::Protocol("Invalid address string"))?; - - Ok(address) - } - - /// List connected wallets. This only returns wallets that are ready to be used. - pub fn list_devices(&self) -> Vec { - self.devices.read().iter().map(|d| d.info.clone()).collect() - } - - /// Get wallet info. - pub fn device_info(&self, address: &Address) -> Option { - self.devices.read().iter().find(|d| &d.info.address == address).map(|d| d.info.clone()) - } - - /// Sign transaction data with wallet managing `address`. - pub fn sign_transaction(&self, address: &Address, data: &[u8]) -> Result { - let usb = self.usb.lock(); - let devices = self.devices.read(); - let device = devices.iter().find(|d| &d.info.address == address).ok_or(Error::KeyNotFound)?; - let handle = self.open_path(|| usb.open_path(&device.path))?; - - let eth_path = Ð_DERIVATION_PATH_BE[..]; - let etc_path = &ETC_DERIVATION_PATH_BE[..]; - let derivation_path = match *self.key_path.read() { - KeyPath::Ethereum => eth_path, - KeyPath::EthereumClassic => etc_path, - }; - const MAX_CHUNK_SIZE: usize = 255; - let mut chunk: [u8; MAX_CHUNK_SIZE] = [0; MAX_CHUNK_SIZE]; - &mut chunk[0..derivation_path.len()].copy_from_slice(derivation_path); - let mut dest_offset = derivation_path.len(); - let mut data_pos = 0; - let mut result; - loop { - let p1 = if data_pos == 0 { 0x00 } else { 0x80 }; - let dest_left = MAX_CHUNK_SIZE - dest_offset; - let chunk_data_size = min(dest_left, data.len() - data_pos); - &mut chunk[dest_offset..][0..chunk_data_size].copy_from_slice(&data[data_pos..][0..chunk_data_size]); - result = Self::send_apdu(&handle, commands::SIGN_ETH_TRANSACTION, p1, 0, &chunk[0..(dest_offset + chunk_data_size)])?; - dest_offset = 0; - data_pos += chunk_data_size; - if data_pos == data.len() { - break; - } - } - - if result.len() != 65 { - return Err(Error::Protocol("Signature packet size mismatch")); - } - let v = (result[0] + 1) % 2; - let r = H256::from_slice(&result[1..33]); - let s = H256::from_slice(&result[33..65]); - Ok(Signature::from_rsv(&r, &s, v)) - } + }) + .ok(); - fn open_path(&self, f: F) -> Result - where F: Fn() -> Result - { - f().map_err(Into::into) + Ok(manager) } - fn send_apdu(handle: &hidapi::HidDevice, command: u8, p1: u8, p2: u8, data: &[u8]) -> Result, Error> { - const HID_PACKET_SIZE: usize = 64 + HID_PREFIX_ZERO; + // Transport Protocol: + // * Communication Channel Id (2 bytes big endian ) + // * Command Tag (1 byte) + // * Packet Sequence ID (2 bytes big endian) + // * Payload (Optional) + // + // Payload + // * APDU Total Length (2 bytes big endian) + // * APDU_CLA (1 byte) + // * APDU_INS (1 byte) + // * APDU_P1 (1 byte) + // * APDU_P2 (1 byte) + // * APDU_LENGTH (1 byte) + // * APDU_Payload (Variable) + // + fn write(handle: &hidapi::HidDevice, command: u8, p1: u8, p2: u8, data: &[u8]) -> Result<(), Error> { + let data_len = data.len(); let mut offset = 0; - let mut chunk_index = 0; - loop { - let mut hid_chunk: [u8; HID_PACKET_SIZE] = [0; HID_PACKET_SIZE]; - let mut chunk_size = if chunk_index == 0 { 12 } else { 5 }; - let size = min(64 - chunk_size, data.len() - offset); + let mut sequence_number = 0; + let mut hid_chunk = [0_u8; HID_PACKET_SIZE]; + + while sequence_number == 0 || offset < data_len { + let header = if sequence_number == 0 { LEDGER_TRANSPORT_HEADER_LEN + APDU_PAYLOAD_HEADER_LEN } else { LEDGER_TRANSPORT_HEADER_LEN }; + let size = min(64 - header, data_len - offset); { let chunk = &mut hid_chunk[HID_PREFIX_ZERO..]; - &mut chunk[0..5].copy_from_slice(&[0x01, 0x01, APDU_TAG, (chunk_index >> 8) as u8, (chunk_index & 0xff) as u8 ]); + chunk[0..5].copy_from_slice(&[0x01, 0x01, APDU_TAG, (sequence_number >> 8) as u8, (sequence_number & 0xff) as u8 ]); - if chunk_index == 0 { + if sequence_number == 0 { let data_len = data.len() + 5; - &mut chunk[5..12].copy_from_slice(&[ (data_len >> 8) as u8, (data_len & 0xff) as u8, APDU_CLA, command, p1, p2, data.len() as u8 ]); + chunk[5..12].copy_from_slice(&[(data_len >> 8) as u8, (data_len & 0xff) as u8, APDU_CLA, command, p1, p2, data.len() as u8]); } - &mut chunk[chunk_size..chunk_size + size].copy_from_slice(&data[offset..offset + size]); - offset += size; - chunk_size += size; + chunk[header..header + size].copy_from_slice(&data[offset..offset + size]); } - trace!("writing {:?}", &hid_chunk[..]); + trace!(target: "hw", "Ledger write {:?}", &hid_chunk[..]); let n = handle.write(&hid_chunk[..])?; - if n < chunk_size { + if n < size + header { return Err(Error::Protocol("Write data size mismatch")); } - if offset == data.len() { - break; + offset += size; + sequence_number += 1; + if sequence_number >= 0xffff { + return Err(Error::Protocol("Maximum sequence number reached")); } - chunk_index += 1; } - - // read response - chunk_index = 0; + Ok(()) + } + + // Transport Protocol: + // * Communication Channel Id (2 bytes big endian ) + // * Command Tag (1 byte) + // * Packet Sequence ID (2 bytes big endian) + // * Payload (Optional) + // + // Payload + // * APDU Total Length (2 bytes big endian) + // * APDU_CLA (1 byte) + // * APDU_INS (1 byte) + // * APDU_P1 (1 byte) + // * APDU_P2 (1 byte) + // * APDU_LENGTH (1 byte) + // * APDU_Payload (Variable) + // + fn read(handle: &hidapi::HidDevice) -> Result, Error> { let mut message_size = 0; let mut message = Vec::new(); - loop { + + // terminate the loop if `sequence_number` reaches its max_value and report error + for chunk_index in 0..=0xffff { let mut chunk: [u8; HID_PACKET_SIZE] = [0; HID_PACKET_SIZE]; let chunk_size = handle.read(&mut chunk)?; - trace!("read {:?}", &chunk[..]); - if chunk_size < 5 || chunk[0] != 0x01 || chunk[1] != 0x01 || chunk[2] != APDU_TAG { + trace!(target: "hw", "Ledger read {:?}", &chunk[..]); + if chunk_size < LEDGER_TRANSPORT_HEADER_LEN || chunk[0] != 0x01 || chunk[1] != 0x01 || chunk[2] != APDU_TAG { return Err(Error::Protocol("Unexpected chunk header")); } let seq = (chunk[3] as usize) << 8 | (chunk[4] as usize); @@ -303,7 +238,7 @@ impl Manager { let mut offset = 5; if seq == 0 { - // read message size and status word. + // Read message size and status word. if chunk_size < 7 { return Err(Error::Protocol("Unexpected chunk header")); } @@ -315,13 +250,12 @@ impl Manager { if message.len() == message_size { break; } - chunk_index += 1; } if message.len() < 2 { return Err(Error::Protocol("No status word")); } let status = (message[message.len() - 2] as usize) << 8 | (message[message.len() - 1] as usize); - debug!("Read status {:x}", status); + debug!(target: "hw", "Read status {:x}", status); match status { 0x6700 => Err(Error::Protocol("Incorrect length")), 0x6982 => Err(Error::Protocol("Security status not satisfied (Canceled by user)")), @@ -329,8 +263,8 @@ impl Manager { 0x6a82 => Err(Error::Protocol("File not found")), 0x6a85 => Err(Error::UserCancel), 0x6b00 => Err(Error::Protocol("Incorrect parameters")), - 0x6d00 => Err(Error::Protocol("Not implemented. Make sure Ethereum app is running.")), - 0x6faa => Err(Error::Protocol("You Ledger need to be unplugged")), + 0x6d00 => Err(Error::Protocol("Not implemented. Make sure the Ledger Ethereum Wallet app is running.")), + 0x6faa => Err(Error::Protocol("Your Ledger need to be unplugged")), 0x6f00...0x6fff => Err(Error::Protocol("Internal error")), 0x9000 => Ok(()), _ => Err(Error::Protocol("Unknown error")), @@ -341,6 +275,11 @@ impl Manager { Ok(message) } + fn send_apdu(handle: &hidapi::HidDevice, command: u8, p1: u8, p2: u8, data: &[u8]) -> Result, Error> { + Self::write(&handle, command, p1, p2, data)?; + Self::read(&handle) + } + fn is_valid_ledger(device: &libusb::Device) -> Result<(), Error> { let desc = device.device_descriptor()?; let vendor_id = desc.vendor_id(); @@ -352,32 +291,219 @@ impl Manager { Err(Error::InvalidDevice) } } + + fn get_firmware_version(handle: &hidapi::HidDevice) -> Result { + let ver = Self::send_apdu(&handle, commands::GET_APP_CONFIGURATION, 0, 0, &[])?; + if ver.len() != 4 { + return Err(Error::Protocol("Version packet size mismatch")); + } + Ok(FirmwareVersion::new(ver[1].into(), ver[2].into(), ver[3].into())) + } + + fn get_derivation_path(&self) -> &[u8] { + match *self.key_path.read() { + KeyPath::Ethereum => Ð_DERIVATION_PATH_BE, + KeyPath::EthereumClassic => &ETC_DERIVATION_PATH_BE, + } + } + + fn signer_helper(&self, address: &Address, data: &[u8], command: u8) -> Result { + let usb = self.usb.lock(); + let devices = self.devices.read(); + let device = devices.iter().find(|d| &d.info.address == address).ok_or(Error::KeyNotFound)?; + let handle = self.open_path(|| usb.open_path(&device.path))?; + + // Signing personal messages are only support by Ledger firmware version 1.0.8 or newer + if command == commands::SIGN_ETH_PERSONAL_MESSAGE { + let version = Self::get_firmware_version(&handle)?; + if version < FirmwareVersion::new(1, 0, 8) { + return Err(Error::Protocol("Signing personal messages with Ledger requires version 1.0.8")); + } + } + + let mut chunk= [0_u8; MAX_CHUNK_SIZE]; + let derivation_path = self.get_derivation_path(); + + // Copy the address of the key (only done once) + chunk[0..derivation_path.len()].copy_from_slice(derivation_path); + + let key_length = derivation_path.len(); + let max_payload_size = MAX_CHUNK_SIZE - key_length; + let data_len = data.len(); + + let mut result = Vec::new(); + let mut offset = 0; + + while offset < data_len { + let p1 = if offset == 0 { 0 } else { 0x80 }; + let take = min(max_payload_size, data_len - offset); + + // Fetch piece of data and copy it! + { + let (_key, d) = &mut chunk.split_at_mut(key_length); + let (dst, _rem) = &mut d.split_at_mut(take); + dst.copy_from_slice(&data[offset..(offset + take)]); + } + + result = Self::send_apdu(&handle, command, p1, 0, &chunk[0..(key_length + take)])?; + offset += take; + } + + if result.len() != 65 { + return Err(Error::Protocol("Signature packet size mismatch")); + } + let v = (result[0] + 1) % 2; + let r = H256::from_slice(&result[1..33]); + let s = H256::from_slice(&result[33..65]); + Ok(Signature::from_rsv(&r, &s, v)) + } + + pub fn sign_message(&self, address: &Address, msg: &[u8]) -> Result { + self.signer_helper(address, msg, commands::SIGN_ETH_PERSONAL_MESSAGE) + } } // Try to connect to the device using polling in at most the time specified by the `timeout` -fn try_connect_polling(ledger: Arc, timeout: Duration) -> bool { +fn try_connect_polling(ledger: &Manager, timeout: &Duration, device_direction: DeviceDirection) -> bool { let start_time = Instant::now(); - while start_time.elapsed() <= timeout { - if let Ok(_) = ledger.update_devices() { - return true + while start_time.elapsed() <= *timeout { + if let Ok(num_devices) = ledger.update_devices(device_direction) { + trace!(target: "hw", "{} number of Ledger(s) {}", num_devices, device_direction); + return true; } } false } +impl <'a>Wallet<'a> for Manager { + type Error = Error; + type Transaction = &'a [u8]; + + fn sign_transaction(&self, address: &Address, transaction: Self::Transaction) -> Result { + self.signer_helper(address, transaction, commands::SIGN_ETH_TRANSACTION) + } + + fn set_key_path(&self, key_path: KeyPath) { + *self.key_path.write() = key_path; + } + + fn update_devices(&self, device_direction: DeviceDirection) -> Result { + let mut usb = self.usb.lock(); + usb.refresh_devices(); + let devices = usb.devices(); + let num_prev_devices = self.devices.read().len(); + + let detected_devices = devices.iter() + .filter(|&d| d.vendor_id == LEDGER_VID && LEDGER_PIDS.contains(&d.product_id)) + .fold(Vec::new(), |mut v, d| { + match self.read_device(&usb, &d) { + Ok(info) => { + trace!(target: "hw", "Found device: {:?}", info); + v.push(info); + } + Err(e) => trace!(target: "hw", "Error reading device info: {}", e), + }; + v + }); + + let num_curr_devices = detected_devices.len(); + *self.devices.write() = detected_devices; + + match device_direction { + DeviceDirection::Arrived => { + if num_curr_devices > num_prev_devices { + Ok(num_curr_devices - num_prev_devices) + } else { + Err(Error::NoDeviceArrived) + } + } + DeviceDirection::Left => { + if num_prev_devices > num_curr_devices { + Ok(num_prev_devices- num_curr_devices) + } else { + Err(Error::NoDeviceLeft) + } + } + } + } + + fn read_device(&self, usb: &hidapi::HidApi, dev_info: &hidapi::HidDeviceInfo) -> Result { + let handle = self.open_path(|| usb.open_path(&dev_info.path))?; + let manufacturer = dev_info.manufacturer_string.clone().unwrap_or_else(|| "Unknown".to_owned()); + let name = dev_info.product_string.clone().unwrap_or_else(|| "Unknown".to_owned()); + let serial = dev_info.serial_number.clone().unwrap_or_else(|| "Unknown".to_owned()); + match self.get_address(&handle) { + Ok(Some(addr)) => { + Ok(Device { + path: dev_info.path.clone(), + info: WalletInfo { + name, + manufacturer, + serial, + address: addr, + }, + }) + } + // This variant is not possible, but the trait forces this return type + Ok(None) => Err(Error::Impossible), + Err(e) => Err(e), + } + } + + fn list_devices(&self) -> Vec { + self.devices.read().iter().map(|d| d.info.clone()).collect() + } + + // Not used because it is not supported by Ledger + fn list_locked_devices(&self) -> Vec { + vec![] + } + + fn get_wallet(&self, address: &Address) -> Option { + self.devices.read().iter().find(|d| &d.info.address == address).map(|d| d.info.clone()) + } + + fn get_address(&self, device: &hidapi::HidDevice) -> Result, Self::Error> { + let ledger_version = Self::get_firmware_version(&device)?; + if ledger_version < FirmwareVersion::new(1, 0, 3) { + return Err(Error::Protocol("Ledger version 1.0.3 is required")); + } + + let derivation_path = self.get_derivation_path(); + + let key_and_address = Self::send_apdu(device, commands::GET_ETH_PUBLIC_ADDRESS, 0, 0, derivation_path)?; + if key_and_address.len() != 107 { // 1 + 65 PK + 1 + 40 Addr (ascii-hex) + return Err(Error::Protocol("Key packet size mismatch")); + } + let address_string = ::std::str::from_utf8(&key_and_address[67..107]) + .map_err(|_| Error::Protocol("Invalid address string"))?; + + let address = Address::from_str(&address_string) + .map_err(|_| Error::Protocol("Invalid address string"))?; + + Ok(Some(address)) + } + + fn open_path(&self, f: F) -> Result + where F: Fn() -> Result + { + f().map_err(Into::into) + } +} + /// Ledger event handler -/// A seperate thread is hanedling incoming events +/// A separate thread is handling incoming events /// /// Note, that this run to completion and race-conditions can't occur but this can /// therefore starve other events for being process with a spinlock or similar -pub struct EventHandler { +struct EventHandler { ledger: Weak, } impl EventHandler { /// Ledger event handler constructor - pub fn new(ledger: Weak) -> Self { - Self { ledger: ledger } + fn new(ledger: Weak) -> Self { + Self { ledger } } } @@ -385,8 +511,8 @@ impl libusb::Hotplug for EventHandler { fn device_arrived(&mut self, device: libusb::Device) { debug!(target: "hw", "Ledger arrived"); if let (Some(ledger), Ok(_)) = (self.ledger.upgrade(), Manager::is_valid_ledger(&device)) { - if try_connect_polling(ledger, Duration::from_millis(500)) != true { - debug!(target: "hw", "Ledger connect timeout"); + if try_connect_polling(&ledger, &POLLING_DURATION, DeviceDirection::Arrived) != true { + debug!(target: "hw", "No Ledger device was connected"); } } } @@ -394,33 +520,80 @@ impl libusb::Hotplug for EventHandler { fn device_left(&mut self, device: libusb::Device) { debug!(target: "hw", "Ledger left"); if let (Some(ledger), Ok(_)) = (self.ledger.upgrade(), Manager::is_valid_ledger(&device)) { - if try_connect_polling(ledger, Duration::from_millis(500)) != true { - debug!(target: "hw", "Ledger disconnect timeout"); + if try_connect_polling(&ledger, &POLLING_DURATION, DeviceDirection::Left) != true { + debug!(target: "hw", "No Ledger device was disconnected"); } } } } -#[test] -fn smoke() { + +#[cfg(test)] +mod tests { use rustc_hex::FromHex; - let hidapi = Arc::new(Mutex::new(hidapi::HidApi::new().unwrap())); - let manager = Manager::new(hidapi.clone()); - manager.update_devices().unwrap(); - for d in &*manager.devices.read() { - println!("Device: {:?}", d); + use super::*; + + /// This test can't be run without an actual ledger device connected with the `Ledger Wallet Ethereum application` running + #[test] + #[ignore] + fn sign_personal_message() { + let manager = Manager::new( + Arc::new(Mutex::new(hidapi::HidApi::new().expect("HidApi"))), + Arc::new(AtomicBool::new(false)) + ).expect("HardwareWalletManager"); + + // Update device list + manager.update_devices(DeviceDirection::Arrived).expect("No Ledger found, make sure you have a unlocked Ledger connected with the Ledger Wallet Ethereum running"); + + // Fetch the ethereum address of a connected ledger device + let address = manager.list_devices() + .iter() + .filter(|d| d.manufacturer == "Ledger".to_string()) + .nth(0) + .map(|d| d.address.clone()) + .expect("No ledger device detected"); + + // 44 bytes transaction + let tx = FromHex::from_hex("eb018504a817c80082520894a6ca2e6707f2cc189794a9dd459d5b05ed1bcd1c8703f26fcfb7a22480018080").unwrap(); + let signature = manager.sign_transaction(&address, &tx); + assert!(signature.is_ok()); } - if let Some(address) = manager.list_devices().first().map(|d| d.address.clone()) { + /// This test can't be run without an actual ledger device connected with the `Ledger Wallet Ethereum application` running + #[test] + #[ignore] + fn smoke() { + let manager = Manager::new( + Arc::new(Mutex::new(hidapi::HidApi::new().expect("HidApi"))), + Arc::new(AtomicBool::new(false)) + ).expect("HardwareWalletManager"); + + // Update device list + manager.update_devices(DeviceDirection::Arrived).expect("No Ledger found, make sure you have a unlocked Ledger connected with the Ledger Wallet Ethereum running"); + + // Fetch the ethereum address of a connected ledger device + let address = manager.list_devices() + .iter() + .filter(|d| d.manufacturer == "Ledger".to_string()) + .nth(0) + .map(|d| d.address.clone()) + .expect("No ledger device detected"); + + // 44 bytes transaction let tx = FromHex::from_hex("eb018504a817c80082520894a6ca2e6707f2cc189794a9dd459d5b05ed1bcd1c8703f26fcfb7a22480018080").unwrap(); let signature = manager.sign_transaction(&address, &tx); println!("Got {:?}", signature); assert!(signature.is_ok()); - let large_tx = FromHex::from_hex("f8cb81968504e3b2920083024f279475b02a3c39710d6a3f2870d0d788299d48e790f180b8a4b61d27f6000000000000000000000000e1af840a5a1cb1efdf608a97aa632f4aa39ed199000000000000000000000000000000000000000000000000105ff43f46a9a800000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018080").unwrap(); + + // 218 bytes transaction + let large_tx = FromHex::from_hex("f86b028511cfc15d00825208940975ca9f986eee35f5cbba2d672ad9bc8d2a08448766c92c5cf830008026a0d2b0d401b543872d2a6a50de92455decbb868440321bf63a13b310c069e2ba5ba03c6d51bcb2e1653be86546b87f8a12ddb45b6d4e568420299b96f64c19701040f86b028511cfc15d00825208940975ca9f986eee35f5cbba2d672ad9bc8d2a08448766c92c5cf830008026a0d2b0d401b543872d2a6a50de92455decbb868440321bf63a13b310c069e2ba5ba03c6d51bcb2e1653be86546b87f8a12ddb45b6d4e568420299b96f64c19701040").unwrap(); let signature = manager.sign_transaction(&address, &large_tx); println!("Got {:?}", signature); assert!(signature.is_ok()); - let huge_tx = FromHex::from_hex("f935e98201048505d21dba00833b82608080b935946103e86003908155620d2f00600455601460055560a060405260608190527f2e2e2e00000000000000000000000000000000000000000000000000000000006080908152600d805460008290527f2e2e2e00000000000000000000000000000000000000000000000000000000068255909260008051602062003474833981519152602060026001851615610100026000190190941693909304601f0192909204820192909190620000dc565b82800160010185558215620000dc579182015b82811115620000dc578251825591602001919060010190620000bf565b5b50620001009291505b80821115620000fc5760008155600101620000e6565b5090565b5050600e8054600360ff199182168117909255604080518082019091528281527f2e2e2e00000000000000000000000000000000000000000000000000000000006020918201908152600f80546000829052825160069516949094178155937f8d1108e10bcb7c27dddfc02ed9d693a074039d026cf4ea4240b40f7d581ac80260026101006001871615026000190190951694909404601f019290920483019290620001d7565b82800160010185558215620001d7579182015b82811115620001d7578251825591602001919060010190620001ba565b5b50620001fb9291505b80821115620000fc5760008155600101620000e6565b5090565b50506010805460ff19166001179055346200000057604051620034943803806200349483398101604090815281516020830151918301516060840151919390810191015b5b5b60068054600160a060020a0319166c01000000000000000000000000338102041790558151600d80546000829052909160008051602062003474833981519152602060026101006001861615026000190190941693909304601f908101849004820193870190839010620002c157805160ff1916838001178555620002f1565b82800160010185558215620002f1579182015b82811115620002f1578251825591602001919060010190620002d4565b5b50620003159291505b80821115620000fc5760008155600101620000e6565b5090565b505080600f9080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106200036557805160ff191683800117855562000395565b8280016001018555821562000395579182015b828111156200039557825182559160200191906001019062000378565b5b50620003b99291505b80821115620000fc5760008155600101620000e6565b5090565b5050600980546c01000000000000000000000000808602819004600160a060020a0319928316179092556007805487840293909304929091169190911790555b505050505b613066806200040e6000396000f300606060405236156102035760e060020a600035046306fdde03811461034b578063095ea7b3146103c65780630b0b6d5b146103ed5780631b1ccc47146103fc57806320e870931461047757806323b872dd1461049657806325b29d84146104c057806327187991146104df578063277ccde2146104f15780632e1fbfcd14610510578063308b2fdc14610532578063313ce5671461055457806338cc48311461057757806340eddc4e146105a057806341f4793a146105bf578063467ed261146105de578063471ad963146105fd5780634e860ebb1461060f5780634efbe9331461061e57806354786b4e1461064257806354e35ba2146106bd57806358793ad4146106d25780635abedab21461073f5780635af2f8211461074e57806360483a3f1461076d57806360d12fa0146107da578063698f2e84146108035780636a749986146108155780636d5f66391461082a5780636e9c36831461083c57806370a082311461085e5780637a290fe5146108805780637e7541461461088f57806394c41bdb1461090a57806395d89b4114610929578063962a64cd146109a4578063a0b6533214610a09578063a9059cbb14610a2b578063ab62438f14610a52578063b63ca98114610aa9578063b7c54c6f14610abb578063c4e41b2214610ada578063ca7c4dba14610af9578063cb79e31b14610b18578063dd62ed3e14610b3a575b6103495b60006000600c546000141561021b57610000565b600354600c54670de0b6b3a764000091349091020204915060009050816001600030600160a060020a031681526020019081526020016000205410156102c557600160a060020a033016600090815260016020526040902054600c54909250828115610000570466038d7ea4c68000023403905033600160a060020a03166108fc829081150290604051809050600060405180830381858888f1935050505015156102c557610000565b5b600160a060020a03338116600081815260016020908152604080832080548801905530909416825283822080548790039055601380543487900301908190559154600c548551908152918201879052845190949293927f5a0391f2a67f11ed0034b68f8cf14e7e41d6f86e0a7622f2af5ea8f07b488396928290030190a45b5050565b005b3461000057610358610b5f565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156103b85780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34610000576103d9600435602435610bed565b604080519115158252519081900360200190f35b3461000057610349610c58565b005b3461000057610358610dbc565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156103b85780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b3461000057610484610e5a565b60408051918252519081900360200190f35b34610000576103d9600435602435604435610ef9565b604080519115158252519081900360200190f35b3461000057610484610ff3565b60408051918252519081900360200190f35b3461000057610349600435611002565b005b346100005761048461105a565b60408051918252519081900360200190f35b3461000057610484600435611061565b60408051918252519081900360200190f35b346100005761048460043561108d565b60408051918252519081900360200190f35b34610000576105616110b9565b6040805160ff9092168252519081900360200190f35b34610000576105846110c2565b60408051600160a060020a039092168252519081900360200190f35b34610000576104846110c7565b60408051918252519081900360200190f35b34610000576104846110ce565b60408051918252519081900360200190f35b34610000576104846110d5565b60408051918252519081900360200190f35b3461000057610349600435611174565b005b34610000576103496113b5565b005b34610000576103d9600435611407565b604080519115158252519081900360200190f35b3461000057610358611549565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156103b85780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34610000576103496004356024356115e7565b005b346100005760408051602060046024803582810135601f8101859004850286018501909652858552610726958335959394604494939290920191819084018382808284375094965061167e95505050505050565b6040805192835290151560208301528051918290030190f35b3461000057610349611c13565b005b3461000057610484611d46565b60408051918252519081900360200190f35b346100005760408051602060046024803582810135601f81018590048502860185019096528585526107269583359593946044949392909201918190840183828082843750949650611d4d95505050505050565b6040805192835290151560208301528051918290030190f35b3461000057610584612303565b60408051600160a060020a039092168252519081900360200190f35b3461000057610349600435612313565b005b3461000057610349600435602435612347565b005b346100005761034960043561252f565b005b3461000057610484600435612941565b60408051918252519081900360200190f35b346100005761048460043561298d565b60408051918252519081900360200190f35b34610000576103496129ac565b005b3461000057610358612a13565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156103b85780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b3461000057610484612ab1565b60408051918252519081900360200190f35b3461000057610358612ab8565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156103b85780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b3461000057610484600480803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843750949650612b4695505050505050565b60408051918252519081900360200190f35b3461000057610484600435612b63565b60408051918252519081900360200190f35b34610000576103d9600435602435612b8c565b604080519115158252519081900360200190f35b3461000057610349600480803590602001908201803590602001908080601f016020809104026020016040519081016040528093929190818152602001838380828437509496505093359350612c3c92505050565b005b3461000057610349600435612f38565b005b3461000057610484612f90565b60408051918252519081900360200190f35b346100005761048461300c565b60408051918252519081900360200190f35b3461000057610484613013565b60408051918252519081900360200190f35b346100005761048460043561301a565b60408051918252519081900360200190f35b3461000057610484600435602435613039565b60408051918252519081900360200190f35b600d805460408051602060026001851615610100026000190190941693909304601f81018490048402820184019092528181529291830182828015610be55780601f10610bba57610100808354040283529160200191610be5565b820191906000526020600020905b815481529060010190602001808311610bc857829003601f168201915b505050505081565b600160a060020a03338116600081815260026020908152604080832094871680845294825280832086905580518681529051929493927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a35060015b92915050565b601a54600090600160a060020a03161515610c7257610000565b600160a060020a0333166000908152600a60205260409020541515610c9657610000565b600160a060020a0333166000908152601d602052604090205460ff1615610cbc57610000565b601b54426212750090910111610cd157610000565b600160a060020a0333166000818152601d60209081526040808320805460ff19166001179055600a8252918290208054601c805490910190555482519384529083015280517f475c7605c08471fdc551a58d2c318b163628c5852f69323a1b91c34eb0bb09339281900390910190a150601154601c54606490910490604682029010610db857601a5460068054600160a060020a031916606060020a600160a060020a0393841681020417908190556040805191909216815290517f6b8184e23a898262087be50aa3ea5de648451e63f94413e810586c25282d58c2916020908290030190a15b5b50565b604080516020808201835260008252600d8054845160026001831615610100026000190190921691909104601f810184900484028201840190955284815292939091830182828015610e4f5780601f10610e2457610100808354040283529160200191610e4f565b820191906000526020600020905b815481529060010190602001808311610e3257829003601f168201915b505050505090505b90565b600f805460408051602060026001851615610100026000190190941693909304601f8101849004840282018401909252818152600093610ef39391929091830182828015610ee95780601f10610ebe57610100808354040283529160200191610ee9565b820191906000526020600020905b815481529060010190602001808311610ecc57829003601f168201915b5050505050612b46565b90505b90565b600160a060020a038316600090815260016020526040812054829010801590610f495750600160a060020a0380851660009081526002602090815260408083203390941683529290522054829010155b8015610f555750600082115b15610fe757600160a060020a03808516600081815260016020908152604080832080548890039055878516808452818420805489019055848452600283528184203390961684529482529182902080548790039055815186815291517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a3506001610feb56610feb565b5060005b5b9392505050565b600160a060020a033016315b90565b60065433600160a060020a0390811691161461101d57610000565b600c8190556040805182815290517f0bbd501ef336990995d82b5e3fd82a15abe1ff10c982757a1698ac5d1c3e79579181900360200190a15b5b50565b600b545b90565b6000601882815481101561000057906000526020600020906007020160005b506004015490505b919050565b6000601882815481101561000057906000526020600020906007020160005b506001015490505b919050565b600e5460ff1681565b305b90565b6013545b90565b601c545b90565b600d805460408051602060026001851615610100026000190190941693909304601f8101849004840282018401909252818152600093610ef39391929091830182828015610ee95780601f10610ebe57610100808354040283529160200191610ee9565b820191906000526020600020905b815481529060010190602001808311610ecc57829003601f168201915b5050505050612b46565b90505b90565b600654600090819033600160a060020a0390811691161461119457610000565b60008381526014602052604090205415156111ae57610000565b60008381526014602052604090206005015433600160a060020a039081169116146111d857610000565b60008381526014602052604090206005015460a060020a900460ff16156111fe57610000565b601154600084815260146020526040902060040154606490910460370292508290111561122a57610000565b60008381526014602052604081206005015460a860020a900460ff16600181116100005714156112eb57600954600084815260146020908152604080832060058101546001909101548251840185905282517fa9059cbb000000000000000000000000000000000000000000000000000000008152600160a060020a0392831660048201526024810191909152915194169363a9059cbb93604480840194938390030190829087803b156100005760325a03f1156100005750611389915050565b60008381526014602052604080822060058101546001909101549151600160a060020a039091169282156108fc02929190818181858888f160008881526014602090815260409182902060058101546001909101548351600160a060020a0390921682529181019190915281519297507f2648a7e2f9c34700b91370233666e5118fa8be3e0c21fed4f7402b941df8efdd9650829003019350915050a15b6000838152601460205260409020600501805460a060020a60ff02191660a060020a1790555b5b505050565b60065433600160a060020a039081169116146113d057610000565b6010805460ff191690556040517fb48c7f694f0a3b9b22d7e61c60ff8aebbb107314b6b698fc489ff3f017cb57e090600090a15b5b565b600060006000600760009054906101000a9004600160a060020a0316600160a060020a031663d4884b566000604051602001526040518160e060020a028152600401809050602060405180830381600087803b156100005760325a03f115610000575050604051514210905061147c57610000565b60085433600160a060020a0390811691161461149757610000565b5050600b54600160a060020a03328181166000908152600a6020526040902080549386029384019055601180548401905560128054860190556008549092916114e39130911683610ef9565b506114ee8282612b8c565b50600054601154600b5460408051918252602082018590528051600160a060020a038716927fb4d6befef2def3d17bcb13c2b882ec4fa047f33157446d3e0e6094b2a21609ac92908290030190a4600192505b5b5050919050565b604080516020808201835260008252600f8054845160026001831615610100026000190190921691909104601f810184900484028201840190955284815292939091830182828015610e4f5780601f10610e2457610100808354040283529160200191610e4f565b820191906000526020600020905b815481529060010190602001808311610e3257829003601f168201915b505050505090505b90565b60065433600160a060020a0390811691161461160257610000565b60105460ff16151561161357610000565b600160a060020a0330166000908152600160209081526040808320805485019055600c859055825484019283905580518481529051839286927f10cb430288a1696de11938bc5362c6f8c60e58808237bce4436b93a8573e00c3929081900390910190a45b5b5b5050565b6040805161010081018252600080825260208083018290528351908101845281815292820192909252606081018290526080810182905260a0810182905260c0810182905260e08101829052600654829182918291829133600160a060020a039081169116146116ed57610000565b60115460649004935060056016541115801561170c5750836005540288115b1561171657610000565b61171e612f90565b8811156117305761172d612f90565b97505b60003642604051808484808284378201915050828152602001935050505060405180910390209250600454420191506101006040519081016040528084815260200189815260200188815260200183815260200160008152602001338152602001600081526020016000815260200150905080601460008560001916815260200190815260200160002060008201518160000155602082015181600101556040820151816002019080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061182057805160ff191683800117855561184d565b8280016001018555821561184d579182015b8281111561184d578251825591602001919060010190611832565b5b5061186e9291505b8082111561186a5760008155600101611856565b5090565b5050606082015160038201556080820151600482015560a08201516005909101805460c084015160e09094015160f860020a90810281900460a860020a0260a860020a60ff02199582029190910460a060020a0260a060020a60ff0219606060020a95860295909504600160a060020a031990931692909217939093161792909216179055601880546001810180835582818380158290116119c9576007028160070283600052602060002091820191016119c991905b8082111561186a5760006000820160009055600182016000905560028201805460018160011615610100020316600290046000825580601f10611968575061199a565b601f01602090049060005260206000209081019061199a91905b8082111561186a5760008155600101611856565b5090565b5b50506000600382018190556004820155600581018054600160b060020a0319169055600701611925565b5090565b5b505050916000526020600020906007020160005b83909190915060008201518160000155602082015181600101556040820151816002019080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10611a4a57805160ff1916838001178555611a77565b82800160010185558215611a77579182015b82811115611a77578251825591602001919060010190611a5c565b5b50611a989291505b8082111561186a5760008155600101611856565b5090565b5050606082015181600301556080820151816004015560a08201518160050160006101000a815481600160a060020a030219169083606060020a90810204021790555060c08201518160050160146101000a81548160ff021916908360f860020a90810204021790555060e08201518160050160156101000a81548160ff021916908360f860020a90810204021790555050505060166000815460010191905081905550426017819055507f1a1eea7d2a0f099c2f19efb4e101fcf220558c9f4fbc6961b33fbe02d3a7be908389848a3360405180866000191681526020018581526020018481526020018060200183600160a060020a031681526020018281038252848181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f168015611bee5780820380516001836020036101000a031916815260200191505b50965050505050505060405180910390a1826001955095505b5b505050509250929050565b60065460009033600160a060020a03908116911614611c3157610000565b600760009054906101000a9004600160a060020a0316600160a060020a031663d4884b566000604051602001526040518160e060020a028152600401809050602060405180830381600087803b156100005760325a03f1156100005750506040515162dd7c00014210159050611ca657610000565b604051600160a060020a0333811691309091163180156108fc02916000818181858888f1600954909550600160a060020a0316935063a9059cbb9250339150611cef9050612f90565b6000604051602001526040518360e060020a0281526004018083600160a060020a0316815260200182815260200192505050602060405180830381600087803b156100005760325a03f115610000575050505b5b50565b6016545b90565b6040805161010081018252600080825260208083018290528351908101845281815292820192909252606081018290526080810182905260a0810182905260c0810182905260e08101829052600654829182918291829133600160a060020a03908116911614611dbc57610000565b60105460ff1615611dcc57610000565b6000611dd73061298d565b1115611de257610000565b6017546212750001421015611df657610000565b6013546064900493508360055402881115611e1057610000565b30600160a060020a031631881115611e305730600160a060020a03163197505b60003642604051808484808284378201915050828152602001935050505060405180910390209250600454420191506101006040519081016040528084815260200189815260200188815260200183815260200160008152602001338152602001600081526020016001815260200150905080601460008560001916815260200190815260200160002060008201518160000155602082015181600101556040820151816002019080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10611f2057805160ff1916838001178555611f4d565b82800160010185558215611f4d579182015b82811115611f4d578251825591602001919060010190611f32565b5b50611f6e9291505b8082111561186a5760008155600101611856565b5090565b5050606082015160038201556080820151600482015560a08201516005909101805460c084015160e09094015160f860020a90810281900460a860020a0260a860020a60ff02199582029190910460a060020a0260a060020a60ff0219606060020a95860295909504600160a060020a031990931692909217939093161792909216179055601880546001810180835582818380158290116120c9576007028160070283600052602060002091820191016120c991905b8082111561186a5760006000820160009055600182016000905560028201805460018160011615610100020316600290046000825580601f10612068575061209a565b601f01602090049060005260206000209081019061209a91905b8082111561186a5760008155600101611856565b5090565b5b50506000600382018190556004820155600581018054600160b060020a0319169055600701612025565b5090565b5b505050916000526020600020906007020160005b83909190915060008201518160000155602082015181600101556040820151816002019080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061214a57805160ff1916838001178555612177565b82800160010185558215612177579182015b8281111561217757825182559160200191906001019061215c565b5b506121989291505b8082111561186a5760008155600101611856565b5090565b5050606082015181600301556080820151816004015560a08201518160050160006101000a815481600160a060020a030219169083606060020a90810204021790555060c08201518160050160146101000a81548160ff021916908360f860020a90810204021790555060e08201518160050160156101000a81548160ff021916908360f860020a908102040217905550505050426017819055507f1a1eea7d2a0f099c2f19efb4e101fcf220558c9f4fbc6961b33fbe02d3a7be908389848a3360405180866000191681526020018581526020018481526020018060200183600160a060020a031681526020018281038252848181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f168015611bee5780820380516001836020036101000a031916815260200191505b50965050505050505060405180910390a1826001955095505b5b505050509250929050565b600654600160a060020a03165b90565b600854600160a060020a03161561232957610000565b60088054600160a060020a031916606060020a838102041790555b50565b60065433600160a060020a0390811691161461236257610000565b60105460ff16151561237357610000565b600760009054906101000a9004600160a060020a0316600160a060020a031663d4884b566000604051602001526040518160e060020a028152600401809050602060405180830381600087803b156100005760325a03f11561000057505060405151421090506123e257610000565b600760009054906101000a9004600160a060020a0316600160a060020a031663cdd933326000604051602001526040518160e060020a028152600401809050602060405180830381600087803b156100005760325a03f11561000057505060405151421015905061245257610000565b600854600160a060020a0316151561246957610000565b6000805482018155600160a060020a03308116808352600160209081526040808520805487019055600b8790556002825280852060088054861687529083529481902080548701905593548451868152945193169391927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3600054601154600b546040805185815290517f10cb430288a1696de11938bc5362c6f8c60e58808237bce4436b93a8573e00c39181900360200190a45b5b5b5b5b5050565b604080516101008082018352600080835260208084018290528451808201865282815284860152606084018290526080840182905260a0840182905260c0840182905260e0840182905285825260148152848220855180850187528154815260018083015482850152600280840180548a51600019948216159099029390930190921604601f8101859004850287018501895280875296979496879692959394938601938301828280156126245780601f106125f957610100808354040283529160200191612624565b820191906000526020600020905b81548152906001019060200180831161260757829003601f168201915b505050918352505060038201546020820152600482015460408201526005820154600160a060020a038116606083015260ff60a060020a820481161515608084015260a09092019160a860020a909104166001811161000057905250600085815260146020526040902054909350151561269d57610000565b60008481526014602052604090206005015460a060020a900460ff16156126c357610000565b60008481526014602052604090206003015442106126e057610000565b6000848152601460209081526040808320600160a060020a033316845260060190915290205460ff161561271357610000565b600160a060020a0333166000818152600a6020908152604080832054888452601483528184206004810180548301905594845260069094019091529020805460ff19166001179055915061276684612941565b6000858152601460205260409020601880549293509091839081101561000057906000526020600020906007020160005b50600082015481600001556001820154816001015560028201816002019080546001816001161561010002031660029004828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10612801578054855561283d565b8280016001018555821561283d57600052602060002091601f016020900482015b8281111561283d578254825591600101919060010190612822565b5b5061285e9291505b8082111561186a5760008155600101611856565b5090565b5050600382810154908201556004808301549082015560059182018054929091018054600160a060020a031916606060020a600160a060020a0394851681020417808255825460a060020a60ff021990911660f860020a60a060020a9283900460ff908116820282900490930291909117808455935460a860020a60ff021990941660a860020a9485900490921681020490920291909117905560408051868152339092166020830152818101849052517f8f8bbb8c1937f844f6a094cd4c6eeab8ed5e36f83952e6306ffb2c11fffe5bce916060908290030190a15b50505050565b6000805b60185481101561298657601881815481101561000057906000526020600020906007020160005b505483141561297d57809150612986565b5b600101612945565b5b50919050565b600160a060020a0381166000908152600160205260409020545b919050565b60065433600160a060020a039081169116146129c757610000565b600160a060020a03301660009081526001602052604080822080548354038355829055517fe0987873419fe09d3c9a3e0267f4daf163e812d512f867abaf6bf9822f141a8b9190a15b5b565b60408051602080820183526000825260198054845160026001831615610100026000190190921691909104601f810184900484028201840190955284815292939091830182828015610e4f5780601f10610e2457610100808354040283529160200191610e4f565b820191906000526020600020905b815481529060010190602001808311610e3257829003601f168201915b505050505090505b90565b6011545b90565b600f805460408051602060026001851615610100026000190190941693909304601f81018490048402820184019092528181529291830182828015610be55780601f10610bba57610100808354040283529160200191610be5565b820191906000526020600020905b815481529060010190602001808311610bc857829003601f168201915b505050505081565b6000602082511115612b5757610000565b5060208101515b919050565b6000601882815481101561000057906000526020600020906007020160005b505490505b919050565b600160a060020a033316600090815260016020526040812054829010801590612bb55750600082115b15612c2d57600160a060020a03338116600081815260016020908152604080832080548890039055938716808352918490208054870190558351868152935191937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929081900390910190a3506001610c5256610c52565b506000610c52565b5b92915050565b600160a060020a0333166000908152600a60205260409020541515612c6057610000565b600760009054906101000a9004600160a060020a0316600160a060020a031663d4884b566000604051602001526040518160e060020a028152600401809050602060405180830381600087803b156100005760325a03f11561000057505060405151626ebe00014210159050612cd557610000565b601b5415801590612cef5750426019600201546212750001115b15612cf957610000565b6040805160808101825283815260208082018490524262127500018284015233600160a060020a03166000908152600a8252928320546060830152815180516019805495819052939484937f944998273e477b495144fb8794c914197f3ccb46be2900f4698fd0ef743c969560026001841615610100026000190190931692909204601f90810182900483019490910190839010612da257805160ff1916838001178555612dcf565b82800160010185558215612dcf579182015b82811115612dcf578251825591602001919060010190612db4565b5b50612df09291505b8082111561186a5760008155600101611856565b5090565b505060208201518160010160006101000a815481600160a060020a030219169083606060020a908102040217905550604082015181600201556060820151816003015590505060016019600401600033600160a060020a0316815260200190815260200160002060006101000a81548160ff021916908360f860020a9081020402179055507f854a9cc4d907d23cd8dcc72af48dc0e6a87e6f76376a309a0ffa3231ce8e13363383426212750001846040518085600160a060020a031681526020018060200184815260200183600160a060020a031681526020018281038252858181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f168015612f235780820380516001836020036101000a031916815260200191505b509550505050505060405180910390a15b5050565b60065433600160a060020a03908116911614612f5357610000565b600b819055600080546011546040519192909184917f17a7f53ef43da32c3936b4ac2b060caff5c4b03cd24b1c8e96a191eb1ec48d1591a45b5b50565b6000600960009054906101000a9004600160a060020a0316600160a060020a03166370a08231306000604051602001526040518260e060020a0281526004018082600160a060020a03168152602001915050602060405180830381600087803b156100005760325a03f115610000575050604051519150505b90565b6000545b90565b600c545b90565b600160a060020a0381166000908152600a60205260409020545b919050565b600160a060020a038083166000908152600260209081526040808320938516835292905220545b9291505056d7b6990105719101dabeb77144f2a3385c8033acd3af97e9423a695e81ad1eb500000000000000000000000069381683bde924cef65f1c97f7c8fb769a20409300000000000000000000000014f37b574242d366558db61f3335289a5035c506000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000018546573742074657374657220746573746573742063616d700000000000000000000000000000000000000000000000000000000000000000000000000000000354455300000000000000000000000000000000000000000000000000000000001ba033230fce515bea9de982fe85e0ec8fe892984bc3070ad633daab20eb370864d5a05deb41870e2117197b84cb71110fc0508fa7457165cb8cb82cb8d4d801e6e3f1").unwrap(); + + // 36206 bytes transaction (You need to confirm many transaction on your `Ledger` for this) + let huge_tx = FromHex::from_hex("f86b028511cfc15d00825208940975ca9f986eee35f5cbba2d672ad9bc8d2a08448766c92c5cf830008026a0d2b0d401b543872d2a6a50de92455decbb868440321bf63a13b310c069e2ba5ba03c6d51bcb2e1653be86546b87f8a12ddb45b6d4e568420299b96f64c19701040f86b028511cfc15d00825208940975ca9f986eee35f5cbba2d672ad9bc8d2a08448766c92c5cf830008026a0d2b0d401b543872d2a6a50de92455decbb868440321bf63a13b310c069e2ba5ba03c6d51bcb2e1653be86546b87f8a12ddb45b6d4e568420299b96f64c1970104000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7cd58ab9190c2792714ab06df5b67e66d9e3873eed251d7beb4fa252d6fed6a0ab1e5fabd284f40878d38f6e63d72eec55c6e1aa8d79c06adf714e3523a1f83da763f4bcc9d34424aba82981534066379c1cba244352042de13168556be761f8b1000807b6a6cd340b97a93cd850ee54335b1043bac153c1b0736a88919bb1a21d6befba34d9af51a9b3eb39164c64fe88efe62f136d0bc83cad1f963aec6344b9e406f7381ad2462dcf1434c90c426ee907e6a05abe39c2b36d1dfb966bcf5a4de5af9f07819256357489365c96b21d92a103a776b656fc10ad1083cf679d240bf09bf2eb7635d7bfa969ce7fbb4e0cd5835f79ca9f5583e3a9eca219fab2f773d9c7e838a7a9ef8755dc22e4880367c2b5e40795fe526fc5d1461e50d5cb053e001206460fc6617a38499db525112a7edde38b9547853ad6e5ab359233611148f196501deafae414acde9df81efd7c4144b8fd27f63ac252ecede9609b3f9e634ae95c13058ad2b4529bbb07b5d7ac567c2da994084c3c73ef7c453fc139fcdb3939461da5bf0fa3f2a83517463d02b903af5d845929cf12c9a1479f6801f20085887a94d72814671dac994e14b2faa3251d465ce16d855f33259d94fcc9553b25b488d5c45fe74de60c303bc75bcdde9374ca268767f5767638d1aec5f6f95cab8e9e27b9a80ddf3dbbe24f790debd9e3baa30145d499dd1afb5662a11788b1bb3dedc1ebc5eff9641fa6918d958e4738bae3854e4cd43f9173cd4c9c821190ec287c18035a530c2dc63077d292b3a35b3756ba9e08295a02e37d332552f9f4fdbb945df004aa5b072f9f0e9fc2e4ed6fe455d95b003e5e593dcbfad0b3b47aa855b34008e0e9a2e1cc23b975a3e6808be59dcaa8a87145c1d5183c799d06100d500227e6a758757b4f7d042b3485aa0ce5e91b2b2e67d3cfdf1c226b7ab90e40f0a0d30cbbf425f495bd5a80202909ad419745a59210e2c42a1846e656f67a764ee307abbd76fbb0c99a702253b7a753c3b93e974881f3c97987856b57449e92ffa759da041a2acac59ea2d53836098196355ae0aa2a185dbb002a67c1a278a6032f156bc1e6d7f4ff6c674126af272fdfd1dcd6a810f42878164f1c7ae346b0dd91b678b363d0e33f4b81f2d7cc14da555dcbe4b9f80ac0fed6265a6ecce278888c9794373dcb0d20aa811a9fe9864fab25eaf12764bb2f1a68cd8756cd0b3583f6e5ec74ca5c327b3f6599fa9ec32ccd1831ae323689ef4a1b1a587cbbd2120e0bb8e59f9fc87d93e0365eb36557be6c45c30c1baeba33cdaa877a87e51fd70f2b5521078607d012d65f1fcca8051a01004a6d10f662dfa6445b2ac015cb3ce8fde56bbff93f5d620171e638c6e05504c2aeeeb74c7667aee1709846cb84d345a011c21c1b4e3fd09774ab4dcc63bda04bb0f4fc49d6145d202d807cc2d8eab29b3babe15e53a3656daf0b022ac37513f77660d43d60bdd3e882eef239bfe13dba2e12707733d56e49f638005e06019a7335d8184f1039ab18084de896a946c23045e5c164dc9d32f2f227c89f717a87d1243516b922e5f270c751f1bdb2b1d3a38a15b18a7b8b7e0818573f31320d496e14a348f979b7606c5124e007493f2f40c931f68e3483a46ab2b853a90bd38ae85e6252fece6fd36f7dad0d07b6763d8001a0d6abee62452904f979cc52fa15001b06eef08f17d6e16d493d227ce9277392337a1c71713603e03803d38d1c24184b52049bc029f4f00b22d2acdef91c776a74aa184cc84b0e764f463ed05c2e16a7a0dcb6c27dd4aeca8aeac1545b48896775ba3fe9de4ea36e946d8f4ec16ca7ae58165e8ddc9189d5cc569888a59733529add4b213ea5c00ad3ed3709c0175b542513c90e18f2d4fa2301389102839d969e9f0d614943fe489750f27382f7ab273f51fcb995f449fa5fba108ad0955ed0819a0a62308021ac4ab0c97f04de9fb8533489b2685447ad71c7f9a9bc89975f9cdde87a3af89ae5bff37d1f192a31b7c5aad50486931bc07820d7dae398960965baba6cfc05c56df18b8ef0f5db488eb87be803fc94e3ad3bd6e4f358fe7ce15ca21c9a4752ddfa98337177a7c096d829886e8d71340a01644c64090c84e88235b11bd1fefe506d59733cdd82286fb466ee215914b06a138356e82c0ae6d5fd8e5fb310eb375540308d95b5d53832a5dae9652f91c1e8c14402991e38836813604dcaf272fc552e7682a6eaa7aacfd4ed1c7107b0232cdee00aef865c5577f2391937b76e34810f9d49fe31e54425b6f5e1d0e436e1366e9762d8295877e27ae495ace18fccfaafd850544c9be949d15d421cf6f4bb180225f7f86ca64480975c486df0eeb4fa80a4632cff28d36585cb5dc534553454ea810260983d02060caf6b1eb2b9443b1552ff73d243fecc9779635ed137a3bc8c04ef13f0329a7a5a54b2af0738218cc91be0ee63512f009435d8623ff4e8cdaf743818510b22e42b586a7e5e75525bb61dd2deb96adc95e07998a265d58fe4df4b9ead5b5f15b9daee510558fbdfae7a56931a6f4c729c18e0d29c467fed504810b7d9dfa0613d1657d9bfa5887e3f327cf46d7059a8a0fd654c60cb9c683c55439cd5186d1615f45f7108f261aff77791cf24c975120acf2b357dfbd2defafac0016525cff9400e0feeddff27910fbf2fa84c35fcaaec90863b605db5adbad0593601447605d68b943249861f8cd33c6419c7611403376a6bb438ee857ced2e6842f99ed1b4a9dc79f835813a4f8d07c14f1ef98773286e79cec1c9ce8c26e00418f1b27c7ef104fc96ea2b2ddefb46e2fec4feef2771a1d7e2643586b6fb97094a8d298de12a6f8f78d88e5d67442ed3310fb40aa6439b89c834e43ecd4a80c0a1d74ce6a90a67bcc996a7e93b6f397fe7ab2fa43711a72b84f8c94bd1e4ac62657b98a4b814d8ef2bb469165464a90d5353aa95d09b6ef4ffef081cab5e9dc12d743364f06d4118a585f7d455fd6e3b01434a728a768987c181409eb939e9396666560d394fb151fc67cb9cddea0a94d3e33382bd0617c95304da97994f110eafaaaff6eecb54421e01dc850dc73d77df18bbf68ecc8b37ee2fff7b6f88c139f7d88d763248deb8b4e16a8fab216c0ce88faea030f3a5c994c6e4ef6a9a68cbc9310787232198b020a7c014a1fa32c1736885603dd4921cd360bfb7dca7aafcbe81d7621dbeb4e5c094c2584c339ce70176d7fd2a6cfc4bbea6b433377eff7320d412947ac774688010369b197ec4d0471b9cc73cf9a3e71bd10901beefb10ca1c53428b89ea63427aae9ede5ba104d3fb54d0447458dd9780cd4e925f1edad33f6f0884cc47da562a3c6e2f5a958a8d8723919c4b88d067343a246c6722b6f9f82018d5213648792f38fa8ea1e635b3983dc1f941630fb3762ef1814ee3f41691b24583ddca585289568b4e64f82448b54797d382916e562b3f4795e2d726facea988249e2c3f72d44ec7197b6f783c6c7a133004d5e131b7b4d6a9557c56942ca4bd1f070a2b46c3a6b81bb9a4d570ac6afea75de65ecd331dff1e0252e0f9095f974f47b2d340d67704343b2e8832232210d2f79665bebccab528745c1dc3b28a78aafa3785c29ce2eb6a8403e4d8eded1cc2554ece0a542aa2febd711164f7d7e3a492a87b01d6b4206e593b3aa6d431e908282fcfee0d14dae4b99176a16fa32f730c2d336dcfe7eff84a7aaab1fc32ac8c2e9ab6ebb72c0306bc6998ec22d6cf20c2b6660cfbbeb064b3047c1cf650df12bd153cd7eec5dc181e46575f07c8e292cc191117cd28302d1f9c72d79b1f4062dd683ca95c3a744ac310764e56b2f02a0c2850a2f24c1b298e712374e9adfe68e5414386d7671bd52f6f472eebfdf51677ce379afe7b8085459fb1e6966f5cef45b256489b7ec8a8939cd931009c8a26642f1ff78cab06a5d25522a922cd5e4541dcdbde4848177a42476b141ce9ea035d28742cee0e5e85eb78ceb2b720e112aeb76cd0eb3fc34574c7476110b3b9dff5c19fceae816715b31fc289c0e7149e8488a59e075ac6683f237886a63a25ad23bf903480b9acf3f724d5ace0ca3a842939d4828910cc735e6513dfc4055624d68a048a626fab6b910eaf558c1b43daf1cf26338bca68b5e308b734b61624c97bf70a82430d586a6c3cf59e1bab2532fd9fa1f6fe4f757c7ede0cabea52f2cbf00cc88ca7db4ccc0ff92c0836e7405ebef2ad2e4b7d3b455d8e4d9ae575d884347bdadb67f5e24058a44ae1335280b671ec3bb9d8247e28fecedf5c151fe892bb0f6e67351752e4b1bf75dcd5af3e62ab4aedc5aa32a1606b4a0de3156b356b0fe74e898065d1e720b81663453fc97f935da3b5755a0629f38d6ae5f8e5e77eb64bbef5fc70d4081ebee7a9f7169df4f0e11796f7a79e9128ec996b6fbd8f6fa56e11f17db4925c27f4cd3ddbdee8a50e0b0d4d8f6e527302cbc4dbeef4b0338e6ac7515c1e796b39c8e83f457b50925c39d405f4cd3c1aaf3188c5ac62bf1dd362bc8c9d4e49d3d2b7c2dd2291fa4bb22d7cbe7963b654d92643b789366d1dce842f47919a1cf5073da8916701f907c4d2f8a710c58e85b59f590123d3f8e57cdc14df41a1481a893b9f9505dc0637ba9b27657b0ceab87b0e4bc742924e6d8bf895b407c54df8622018417f9e543fe49f5b10a7a5fc66e5589304af33a20ea108ddf63facebcb20d22eac2fdf4a97285ae6d3f87865fae1331d00e631dfe5366345e0d78bb39a8077484a941176bc63f469f001cfd230347580b6226d6adff5ab112dcd53e7118925296b1a05978a703e383e6ffa5158fc36781f74501564992ab244d3475e1ee8e7146033da2dc116489b84c378e4a750947eb9ccb982a197f13976bb105c81624618c697f32a5b9e03f3675b2315fe773e4922c2e3da7f68ac225107405ece58dc6bbe2bd8947f3e4269ce245589497cd892c750f9ace0440f48057090c8a6cbd5046d3d982d634b4ad6ba41c7a38b7b8b0f91cb6898e769479fc3c7e7d2010b7fb38ef13c17db705a36455a34969803323806009a4e141a5c42da0f7a5e4760d07250d7e483ca6274e57cc2885e5728c24c8b5102845e8bb74b1c394fa7a206ec052c953967380d64c148ca480ab0edbc5da1a7a1e649c2ebfd19fefc52d81aeed7cd83f3c1d2128bd66feb99d5d8fbced01383d2abbf9be47f3390dd336c22b533a731d1c59c3bc5361d781ca15430d84f3c67d6981ab99100f53b6b5623df9d8eecc99d24e02d9301d636c2d5988e98a54339d5b516379a67d50dd9994a28fae5b806c56b353a84cb31729487a6d9851960b83ebc5178be689720a80c5c412e67f8ed55724534c92ab15c3bbc5bf13dfbff02d41ce4c9bc112746b62dea2b21d034e9a31e276eacfeeafc672b95e701ec0fc7ebd4b020a73fc37361b3f136246a0e3a8378442eb5e60abd7da2032dca9b5556aa22e5007c901f438c5e1baeb5d3ec6128a84d310363c6ec17d4ffece27f502b5c63d20cb1d11d0cfc316074faa820a03e6c577389e5e82ebe5f0976b6f5266618f5eb56986714d5cc75fe87176e92dcf01c58029d2b838022c0812c933db17dc4566d233720075065fda26f44b0ed3a46b6143fe180b7a1e6c1558f87b875aedf8c2fa968e2c925f0c08c7e0f23a9cf1b46f7955d9f1db300dab801f5672e2a7231bb2b622b0dc0dd9f2ec64a5f10c239e613247f8685369ed60b2d262c038fcc43924c5aca318385c12412b10d89753f9dfca43eff5f2be7d7d7b2788b877efa8b46ec5c9e99f922839bef71c613cd44cba597cf68de366eaa8874032c14d8012b41e72fd66422f7031d26be0dc4fef8f36a3c124e4ae767a665a94233812984c4466f5bd698b5fc22153c9c2f4110d9defb23c00e722692983b32ee0e84514169910bb21b14066d048960b29b3ff4c090dd5723ca4dcdebd207d4f88da831f0ee7de4aa302a06589a4aba3ca696e7d3c3e9a93af79db91f7a06b0ad825a8652f74bdb72f580e9afb31aae58807e24067f08dd719abb4e6e458bc8aa272d7a5bbd00710c43a1fea220b9022a26b574997517d04573786a4c3e09d30f3ec32f328462e26d4f7ff015121758ce1a2fd51e7f419eb6d8ac04497ab812aa6ba2e981a312ca16c38ed887b2342b0a91348198797919671a23e2b0634b523f931e48ce0d8eb840c54045d9193afec069803901e5ec1108782503cabd0f43373a85acacfa8af44ef2b1d09e4589d2dd4fdcefbf435cb61254f189ad433fa6a4e190627732ae4ef2b0c85cfcbbbaa0137033034e70a3906112dc76ec101f3198e25fb38aad46261d6019690dbf059d66c44e7ada244589c55edfc2e7d18c0ddfcd2d3841bd54d8502763cd0f4696d44686ae3be29ba3063ff6e7aee14de126dc43302f7c0b57d59eb4fdfc4903ccbd3f7309225dd90b5f25c5ade49c14334c0e00fd18b1dc611b10fbbb98c560ad4908842e765c661b9bce005aeede6461254338b8dad3203ee1b58bac1062c7e02e2aa6d420283ed81525839f2c8ff54ac71cc105042c594fb7fd7b55c14cd1247347a197ea8f93c1bbeada1dbf3e59b798c9b15765ab23f856fcf4eeaa5892c3857646bcfd8ad2bf0a15607e0d6696a8548da32955f1f8476f8a20fe4f59b3e9bf4468730b8d46c824a370d37695d1bdcac521032804c5cc66505637701e653ccbddb052f4ecf185b3605d0ba3a4fd99161973e36a35bf79571841ef7506db822dd2a5c959f36418a8dd8acb5b3ecbf3e7918a73695501ef8f440aba43c6e4575880ba3bb83e0a839254fd8d8c6b979d79337a68d218565a5dcb1518c6c82aa73ce7f54a9434ceb5f5fd503137164d74a230e46ce298b98576fea88806bc51e393acdb2abac1da23219b4dbcfba366d834d40dd8e616d214c3478136050555539eba776bf506870c3d20c4a4645b9a7c4ffa976534068009840aadae71f578ef1a325717f64dff840b9dda81b123086a47a172e6793e68af6140b1492058fecd68c4c23db1cc13d2b57f52d0cba89cd4c26d1bd580dd2a054a1d934a80b9eda8ffb503b7e3e62d00a3d075235410149e976529d8029595e4daaae1aa685f3cbdac9b26916320e75b0846d2de8673600212bb648b26e3f1709df425136f33f46129afc90839d24de1e9fee51c685db8a280a5dd4c3ac1539664cc36ffd4537af480d4082146e7395cd6de1f8b652bca8853ec742366702afd6ed79a5920e4ad1317545266f6dbb796ace0fdc731997cd94e1bd8e6689c856adcf153909cfe882b9b02650f4f9eb8620983f0c6b95b3558682d8134a9ec8fa97e174173041115b2eae21fa0b72d0a3c7c2bf9b022fa141a8b495de8321c152b0a9a942c5baf290a234ade4e8b579238a627196fa5621b196ecbe31583517ec4ed82e8d3fb21a892dfd65ccfccd2d36c5d32afa4d4bf201d684c4b1c8c1207db455dede5b908ac63d5fc0bd2b36e11df53bd52e5ce27a9af9444a8cc4391ccc82914b79ba2971ef4ea5d5c30372e7cdbe9bedfcea9ccc8140f8c3ad1bcda29d11fe51affc74f17c9832798e10222701e0d6e93fd109cc9a12df4ee5d38c531574d39a9f4357a60f8150ee509c68e469b4eb0e9be2e6ef9099f1bb949f738fa801d223316fbb1e179b74445228c8b3c40440306e4821077860c37d6b8c17230fcf7ea48d0bb0d98fd3f1f00655e11a8b2e0a7d5da8427784a8fc6d1a2d4d1d3adcc02030b50a700788ce4078c199fc733e2ad469dd9c775d7a8025b4db9b960619f0263b7f09d038cdf85045ac2a1cc5a18364048bf242af713ac4db889489d781ff16b1dcdf66acd89bd6c7651f25a17ce751b67697739dc4d1a125fdd5a8ecbb0cfaf31cd4179249e91171ef3e628dda697afed9d09b53260ae475d59ccb45a6ffd85a2c4241fd134462cf2ec21b51422439aac77954d1b2396761f16e1c6e3242b538f23f584b95cd4b811e35a526748050a7eaa02cebdf8887d94287c99500bf9c2afb7f36ff47e17906534097b02f10620958e889d2392d30660e513c22f580a505314eea4a865d97adb9136c495403e321f425348b56ce8f8e8e91ccd702ade0bbd1efdebef8344bb9defd471ef4b214976556f59f679e0fa39a2007bb9902f5a60ba044c4316c27f6b634241acdc3ce437c4fad599aabba291bfd71c05eca6d9df49abc33ae7709f6622e516c22418e7ab86144f6baf3697bfeeee65294175e5dc9ce5ec82da64537f5f5b83f5a938e41fa8f6f97f9102fda8bcbfb6a5c58f79648b97e948a074e459b9b75a1793cf7d9ca5d7ab27cf7035ece0612d348a23c0fed509c5e18d19b1e659af237c3b9aba4fa8477de805c5f8ccd0cbf3846b6ee1bc9ef76a190952115bd08a5108c8bba76d8d762184c122d081c6dc8b4c49a7f0e16ad4cbed86c6818d4f22c03a100c9afe3675a2f354bf1c2cde1f5e5a63b95761e10d27c9482539387e3aeeaadaeab59faaa20cf595d4d8c57509c751446282581ed28cc55736211e6fabb63d0f299e39ac1cd2af1431bfb03f86e5e59691dffad4e275d4611cb2d7d3be3defcb77907c94db86d989a2ca7e19729e3454eef23b0d58bff8203b08f41b40913f2d2dd2e8c98af09e5aaee76030d8201640d78e7bcfc6c1171e04cb39a6bd060ca41ebbfd090883d8b3569c39fc19cb5d87c15062c9f09138d4e3d3f3421227fb2ac48b224438b12702cb67e2db161a3c771d866c3cc55d15a094f72fe314092e846256e44a1dc513b02bbdd976321f470f81f36e719b9acf22179855d36ad0c50dab79da662e9ea7f9685ec0b44817271ffe2b7254ab7f3ddc389847e17edbd33fbf789bcd604ccca0c01c60deca286858b16dfa17c5875916e0159dfd4f0495c08bf6de51365e2175e47325d5ee71c96ea8ce24c4541886e0854bf7dd8a980aea1aba9add0316f3d052a2eea95c02c241523f3274ee62c883c4ac440d7626cdb4f0aba7a4ea686b2778cd7d7be220357de63cce55a3928aab4c200a2cd65b04d831ba0b54dc91cd6ea410359512130d2a0122f3c9752ba6210ea3b115caf891f0a0a7ef210d1988324a9af926cea8487640a473aefb2e3b4b9259ca4da66089d7f7800f87cb2bd068b8c268dfac897b9a2dd1ff4ac2b19a48b7e95a39ebc6afa2dceca7928ed8e43630d673e5c7ba1fb4afbbd40243ed411b6519420e738c24ab183f900872f10248190358636c789b842f156987d0593fa7cb813f5c688652f871aada7cb5a9c2e15ddedac147151b4d5a7bc4b33cecac961a3487984918868515ca73ebc647945fd9044f3c085b184b3f9d333a7b74927fbbe4a0d846744e0fd6bc36f9381f76422633946fe79e64c3fd63e30096ef400df8cd8c884bad1955b82c013c1a190db92699d39217e46d3db284f35b18b782e791d722d12b85c8a26ac98e9dea8356f9d3ca58833aef4ffd883953f24c96f5351438dccf33693230db5d72389905b49d7308cc30b805fa968532a976009a527bfce9ea921ff4ea9723be5b5972ace8553441a4dac7f0b2114edd3a25666d70c4f94131a63f4521dbd004309157bb32f9fc649058ffbe747bc3addc523f805f1b34787b0f446c9ed1d1966550c7d0c10e342316c6b34899064d0d2dcbb09087ac20572103ee01193a3eab06c06e3206cd60bdbe367af81dee5ab3e5dde9836c558e54c9bb6aa306a609225cf25a65b575fa97d9c962b72b798e9a7fd8192ba879964cedf623d544c8929af5c8dea56721d25578434e2b234289895c697c9c1bc4556e4f6df479a837d1e9132c011e47f9e23fd27b70e7601fdd24f28937efb9e46673b9f56914638c793f5c3b625664f2b221afb3fce5aee92a84d45bab5cda58c49777f82b2b1c8293d727fec90dd73581b087367add474dc7b4cad75cea1e43619ef3fa1b35175f5f0889c031c2083e764b0f4389fffeb307831b73763e73d2c3112adff579d4dcfa1c09d3f2c5927568a70027242e6bec83c5e2cf7e125d8b5e4ad2ec339fb79bb15b8b9a6db0ea9408fc6fb8ca6efe9ae0c8c25900d859b17fc44c4a262c7a5e06ae9e2083fc6dc36bd08d648e9a1a3d8fcbedf12777d690ff15dc7096e7c8b33e71b19005c9e1b20d2c2b6f5c7c1204edc691b389b6ad04f896ed297922bb92b9e6d10a2df2a83dc71c15d2010b595c72d5677017d6d7938ca3538d671e13b8496583b4f9fa59fd481f1f438f92b01a6c5f7169d44b93c0b6863c1a183e871e7f50e26e6d41243a1c509d423309dc886dbb9ac245263ae9d6024456e72b57e17cb08ef00f4fa4dd9fd27de0685c4c6c680ad654e3d81dbb450f0a5e7821412d442c2034093e3fb10234e6a51b98fd388eafd0eec66b42c275a3547f72c7f3d16ed81395e9a2664faacdf99bb22327280e518e4ff047451e6f7420b562c68877c96e129d0cbe18896aff48d49da028dc97aa0108da9b29c540c5238d676dccafdf463694aea34ad4f513b6c7a58d071c335ff1313d41b7cdd902904b8c9fbea2ed34878b407ebb8144f603683ce4ce61eb0690a00d492978aac3a0f3010b7479667811c3332c06553c14809c723316c84d084530e93a63bf0b7658f7bf367d29577236e23ab658a685f2612f0216a932a24aa4f70b8d0609aa9ca14e4d91b8ed9fb62864ded646012ef675ea359117c07f528d7dfb742aab9ac892851e97c94f72d5c34d4feebc7f67e09fdc6f633f050833192f15a7acf4f8c8beb3adf3860fb26fee39a416ec362e4b6d9ced09fa57b3d5b7fb7de018e4fd93eb65634c08f6d4f1e2f490c2a8b1be2794a27de0dbecc9949fd1d5eefa0fc6f0033a2bdecfcaa267280b445e92385d2edd4c2b31bdc5d54ddd6cb30b3c370a893c217945d346d1c5b8b98ac754a01afeba6f5526939ccfe9f2432461a99c7b9b44a3983eb65fb064c32f8c72e18b8f6e42e72a1bac21b3cf94526f81089b235794412d1aed20f48324d742d4079e9546f495248cf7f42839852d604598ca2079fe44b125ae9970973b57c156e83fabe6d64c9aaab5c243d1dc71520d45317b913205979fe5bc075b0068d8a5ceb7c8ff9149c763c22b08d35a09feb8156bf7d8eda212a102906e251efcef1ebed894556f18444a0938b4c050f2b873505bdce97cd4fe539a944b94e281292f38850dec9e9f108d3b2d5a83837d114bcb3d6e6511629f310d194328eb05a7b88e7a053e97dd92881c89a1169e7d23a4fa1ebf532eed2579fc4482b9c93da2b5e9619f289f346160996cc61a3f380ea71b25e777af37dce79039cf90a2bf16ddd46733fe9c1cddbe7a42fc5faa7869c96ec463e9817495bc24a23cd9968213927522ddb0d6ba5db92f5736a5723135305a6c083a9bb54da7e43da3ebb07066ad94e597706062118fef17e9e65363f71d8859d30527a495f06bb025c1d26c6fc80e9b140c7108c57ee5583063bd8d2a7efe6a3026a79f2294e09ce980be8ce1a017132ccf48a63eb32454b12506a6099d4e310f07612e77da46aa0caed8fb0446fd6091140db2cb1432bb93cbf681cefae9d849fee6b0d87898d52d31a209ca6f168b6305011e2c9a55fc5ad2237d7c2d06b98e0703ff2a89fc7af8471aecd2a6cc0a4745082db863bc8d46209d51135333a03b328345b86d6cfc23d6d7384fae5d8546f05725ab139e2c25b0dd9b2113b2774391aa058cf90915bc97a94e74ca0ff6785243122f12decdc48aaa8ff27200007f35e928e62269f7f07407802c9a10648a91180d559c5c37cf3f425c9949b9e38ce4c99b71810babe45344d929906776a66fab175e20bc5930f1dc4b5b888301028b6e0f92293e468d0c6b191f0840ed822c036e6257bbd4f0db8e931463826c0be855add67bff5fdc6d4de7347fa07e63d68f4b6876774a39dff1ae927614f8a879f128713e24b263850f1ab3176ed0e9ca9369af947bb8e862e927cf803ea7b53b68eb8c5f87f1cde2399122b7892ccd4071610f0873981ece2ed719bebb0d508037e46b95610d14e9a826549cfedecea1d32074aa439592929873b49d9434f35646adeabc8b52e323ec2dd6d0d6e27b530361fd8bf9e4e3a0a58e3079dc63156a684bd5cde53ba8c9c51da274bd61cdab187a3fc0a84d5005319f05fc7ddbda575f73f3178336413f8ba0b99cbfdd5c350a3a925260284d75fe06371716f951d76078df7cbe6f25beab46b8f4222c74f68822d6747314b688839540d3bb9bd0f45a028e780fe2b5c78e28dbce66680f1e57b68d6088101146aa9f976bad10933e4f5481444a46d40413ae5d00044a29dd3760c712c04771976280f793ac5bf8cc1187976096e4620d646358f207a9166b9d27030721fc00688a0df926e6f4944ba6e78dc862a8e55e3d1a20d2993d8c8410548e9bf1b6efa181daf8bc060bd1af3dbd8853d6d3f54bdd1f6270b20fcf7f90310109b98f6b366a4ebc6f717962e408bf865d0128fc9ed607f848d376ab1c50e66152f74916a28539a762c75387d144bdaf4a0b8b0e7baec532e8d531501674a8727547916fbcb2e45f9c7d41063bcfec3de1b0adee000e555397ab16fb0977a8c3ac1385dfc89eb7db5cceb9109077d36ca9ff5fcf9feed6b985693746a95ba34f7d2875f61ee8606302b6470f8ad17b781daab036e288e5ee083a3a36eb116a34f5ad97e1675181818289f514efe868feeec3b48b1a574b9405668aa536e572f0e2b46fdfccaea5b2f65285f6a9a05c020bf440f5db912c8ac289c67b9d724225eff88366992f08711f35112e66b765872d39b54cdb5c4c0719b2c17dfade7e2f19281e6ae7885708ee8a8f6f90ce79387e6e47b33f15f212c5b386a5aa5f93cb597698dae4b5999ccb4d652a08c41ed27c45d2ecbd112a679374ddd6606ca76ceca9ab08f7f648d248622ddd633dfc121f9470930ae058cfa9455ddbd25a38aaf48f242ab6e0dc895c5b2af0d9ab0c996df526f144cce6297af5f3ac5fa1d159f52e072b827dbd273afcc6e3b8fa1151acaaca5965a4b6cf5b0ea6275da3208159c6bd6d716eb61309eb4ddfe1bbc4ef8d013d477668cb3506ebb4724ccc72affdab79dcdfaaee55a5946b4a3f768dae9fedddedc6c5712296f26c025ed2ee299cd15b1e692c616094f500fc53fcd9838401c0ea6b6ccb883c149a52d875501ec2e647b1d6720a8227e33cbc1f429ef60103f3334e3de2e40ed4a59d811b8cc51a695de25ebc66eca519222dafa22dbca634220097b1d3f9aeddc91d11019d7215629122b4dc6e3211ad842288b581c31e44fa79e1f7855d8fa77e7a224cf571aa3c16b5f4fe5feb16d7d1bdecc543b0e8ff01c677ec6801e87241ddaa02a5c83bbfd1d84c62e269f6ce8a708e693b86d8e5439f129431a4c1c0bc6ad47784c38e1cacf6c523da23f65a76c264b96aabb50aa9e299be6abd1c9d078ac3b2c5f2c3986b5707f143513b4ea91a2052731ef5b48780dd0cc6626a0f0c358454f6eb36df7caee6f8dfb3ea19a0ae79c0d1587140147be3efb2a0da1305d5fe056010c518e3471572d889304c4ce00acc78fed04a4b888d5e7e57d6cb5cf4e5cf1f8782e1b25ad948eb3e443db75af9233aaaf6659adbe0ef33d4b3ba5214b85e656719df2eba42235b2e268f80e3c5971d28957f8e93f5b04a3d5eaa607fd4bb838ae48661bd093342762cfd1ed60b21f04f5b95c3e5426ca6127b04810e2ee25bb56ae81d7840328d8d4f7d1bd341ed58b102d9860806f4a4d117c044f472c85ba422eab084faf8994cfe0a880bc46dc9c1a8c11995610756e2ac50c5fea8ebbcb53dcc76b1944ce364f8878f42310fe0f8cc211c62f627d12b20527dfd84b78c98b1122050cbcbdb70e08010f68294a6a805d3fab97e76cd695f918e73763ac2c3dfe4a8d75db87dc37e2399fd854f3284d29c7bae3d3e31c4375ad9e047f03a5204c2ba93b6025c112ea2c9fcd731e380a8aaa42860c859c2e2cfd333f0bee741e21f78776defea86e862711f0d0bbf64003ee848a8d1a12dd00c024cbee343d1093e653555c033c198401caeb951860392b5b1eed6200828aa310ed466e41d855dc4231464adc2b6b6fd66e03fd42736fb791387efec28b37d0686272a6bb181a621aae7be06866bdc1c4be69e94642c8d3782f5ab7cc8c890699008b52a11b149a517771b93bc2ae597dedaf0237ea8d9674e26fc75c3b468e04e2fc317d03484a75fb274f7ba1617bbb72ec16da1fd4109952d052e9de7c00761736dd17e70db0976692626ccf8bc9e88ad6c25ed88a2f7c2750add4ceb95744f690ee5f2fa423a2b62ae57c1105958bd8e81025c9412fa71f5d1e81bd6cffa01f489fab7e90ab8a3c8aaffc8e3d594beb254c460347196473117ec2a416dea464eaff95da6cec26b5535954901298f11932ebeca52aded139f2d5aa2c24174e2f6c701ce1f4564c60861ce3b9cdac1cfecf071295c5ec581f0f075096fa457373c124b6c8cae3aaf915e4701ad94ec9c01e5ca0552019bd7f107a7d5afab9e4a5e7cc7b4c5416656ad064f4a0f89afbf7c5b884b69a12fbce8aa73a49b2e5c5728c67a7396bb8341afdf52213b2f7f8e84962cccbeaea63a3c7b24881ecdde39cc57b4f211cd57c6f982217758042f61b648496e62b612b7b8bbe1b9f15d237aeac42b54d15166b5c71eb27ccca1fc9e050adc62a267eb82ca2144ba323a73aa11e2fdaa87695c70316754faf7aec44a49b668362b0b35e884019227e7b9a35e8841e64e0009c713d7f3e4a74cc3feaecf4c99b8d0ecd85c8ff89771b63a38e3af990641f28fa7e4ea560577d600f43ccd467d6a347fef04d392d42f8e97659348c68b41299f94db4b713d61868adbd20a4db74f61bd0d1e7846bfc8b8f8bb50bf50c2fbfdaa87328933741aa2b1ca50cb759c1276f1a7930952ed656921f5ce5569ed16b31b2a1b6009c784199ae60ce2e35d573808a195974536f220cd14dd634bd06800435cf1219047f6246c2d9bdea5e489ab4862f0cb0f01439ad2ad1e2042b3f63b8611a87efbe842613c21761de4c79291a8491092c20134252b8e900e5d3cc70e75d32cc41452c5c33b66087213c34f67ae73fd56a183be858f1c3bcd73d814bb9e3f78cd18992b0ea401d8f25c3b60c055df8e6430b62899bc86167d0b5e2bbf16d75bf3f2b94c26542202bbfa0abe99be1a07c78140f42c12f51576007bb5439966a47cadf5c4ea624a75e7a4f01d8733aee57e3497c013de4a33cf54a94acad9b1aad837865a6881db9a725310eed49581d2223f2b0984757bf3fc5122c5dd572ecc781b48fc508122775779d2b2849e11684a585ce844d21352f8d35ea53f0f34d772bd9ca76cc4dc33aa3f2e72418c097614fa5260eaf3c2d724d3599dfa0991a9c0eec9c4d550886c85e1ab2541e9868a36afbe0d9c07c93e44c4c73c66f88e770e5d4e4ac331fafc6870c928fca85756c444c6e8f6cf75865859abf0cfecc8e89b8c806a2e6af7cb752215bec6201eeb41759b27d599931dc2ae75d605b3e387bf263ebfd09ce2154b81479675555ec74ad85150f8eb8c1b3c4f31f6409648f9c1b4678c82e8e2afa9c887f3210afffed160d1634ab0259e1bf5565d8598605a435bd289afbbc12034f67199b67bb0fddb4b9180908c483ae5a8eed16221687e1f524d010ce5db78d1b999069f225479fd6bf0681c7ee95d4665925bc96399989b85284087e67d5a070f2713feb78bcb91bc019f3f19bf3abb7cf36ebb98f09fd64b61e2bddc9ae6335da48ba85b62562726e142bb9d9e5c8f278dbaa0657dfe3e410f03211a072555624d98790aefe8e7b0281ff6af3de79dd5a414632f9d4913a480e9cd6990f94350304f853ba5679a4cb3a647b98bf1eee6cf70f77581a1ff82a9ffd7296e8fd172d37b1b0d1621692cbfeff8de18658f04af5d5be08bce66e5dfec5989b674219f9ceb6a1037c80a8febdfac63d482debd34c3057a677420f0bdd66e2c2b25a9c1d34b76b4a998ad3ee21d1e49f812422c83016c12c201ac2b0f07ddc00638846f215bfa6c575cbfd577178eb0282ade2c459a13386f5dee8a7502321292a7de077f4fd12967b8c8055596e7a43287639843b6ebee58d463fa044562ec2da7f9c2a7f28cce685178eddd3b9fe7b10202997b6b170555a71555cfebd06cba6bb019f8cfac2ec5db3b1d1ca88acef9accf76b6a74600e590a0eba1c839d6a577d3877e7d6d010b04fc58e160ec9733bf200a9e0b24fe8ef32613cf2c7b1515008b8833e34d3967ccbc8bbe30fd1810f23bb153b814392eb37d8917e96260b3cb16895ef13b96d72c81a14b908224571680dd56d04a59a6583a232ec58e8cff16f6428b5e3dd19f362992608aba912b642aac9950777627ffa4eadfe9f31b73c3fbca11d2abb623b732f3d7c296806151257c9f2306dee1c84eb05d586e7a82a8750905716b3e51600250a1e3b4bf274130a1bfa47117cc8b6db3741ba04d977015b8ee250c3ffaf859fdf0372b88fec188830b5870f251889584333547f3436a548801fd3236da2ccb2b504f85ef1d259bc3e00f0ced934a4b297ecce0d668fb3ecb524d3ff4380a7856c7060006de31931d0b26ec1d084e0dce3b9a123741cdc326b441131d777799623c6340410c331c7e8a4a8175d7d250274cc4ffebd5d46d855bf90842888893c348f0a447998e3aaffc81c9b65e3a772eca5c2f0907ee13ab6a2babe99f388755fa3ac9dc79a2ba4ad7a869a876448ed1d4dd6a8c678065cfc90df8470b29c83719bfcbec7c5e3244a665a28593ad42ab84663bccf570a8e8b783565f909b5e6e8cd69ed6f79fc945ce5d845c998f25b9dc118c96dd2c0f592a73497dbd9e050632c8d82656a71460d0ae7f5f38636692a78083b2fffaa517dc2dfe18ae020e6a5562be54ed9046c7129b3a57dcbd1917efb0579fa9a3978690fded8e52e4860db75b2a93c77316a6e84df4965291a7531e2abc0fcc0d0016acc29680baa575cb7be1a03206236310eb5120ab4069e0f8f0cc3f6bd188ca91963eafc2bc66b1a42f8c49359cf3171a72eef94eddd8aab03f770cb2f489aece4e09a85fe6b9790ced5feced19e4cfe6bcafd1a5d99fe56b78f7a14fdea11fd5e331e23191a3f74b32d8ff2740409f346aedf469eb8aca16b43dcc44c400ae3e6d1c4717ae1f18a2f70830aa0c4d5734922374dad8c006ab97e02a4263999ecad0b1e9f24ed0b599467c962932ec610e63c0b3ac845f5d4d10979c92bd884669908696172609e0da039728baa1f0dca8885d5439ca420e87f5c449908b2a5f69b65b60adbf5d74b21eb1f4e0d79558c59b4499c245a9952de8d3a51021f2e77c44e06a489df3b72d28e5d03ddd358ced4f5a1fe057e58b86f9e717cb9001cec6d6665cc0f5b9cf89873e6e7d10355746e99494766c937683684312b630337d1c411f3f2eddc52a8267e19d38ee12c810cc4e33193e26790b13d1847c56282ac86697996daa386b06ec2ceaa97fac9c018baf644622c74546177267b053a82292c1a1cf194909beba3f2670acf1d095b0caed4b8da2fe48c9da3dc61969d938707a62ce9cf55b89ceaa04a9069d38f4e89db794a335933c5b45fe215976e76dc71b7719c2ef29d06d2dbcfce0470007331a221dbce6baa3f418f989d7dd927d343152ee310d084799300e8d3801f9d464d9bbd5687e3203cfb8e589fbab39ad4851b07bd13b29d7f4b767858d13c5937a482207470f673593aa9abe339b3d63b7ea4ad60e51e7f9080381eb07213ad1996ba7bd28f8b44b7ea037e0bf9716f56820f908fd4027249df11aea06df25b3860cb18b68a7df5ed0d14730035291346049e1e5cbdefb30719548fde4f986bd9871a71b5bc7f6e03ea4fcf1c6ddfecb06413832ac27b08d203070acdaf432bafdb288908dfd673caddbfe41af8255ff7106d39db8d003ec1abcc3000bd7fe1daec2624bbe8417f81150f20a8a48324100ef1570a6de7c0a21e16f6991b23016671bc96ee55e99a97a5a0120af8ecb816137d5f40b9e71d56cbecf61569dcd2f850ede77437be06fd85b54d7220b9bcd13e682a8227c7a05a4efc8d258b0331b0f47cf45ec370b491d6b2e4e601e50483480d9437fdf570b6be69b28b964972fac047f8aaecbe567c8ee3d583a46d5b58fa3c361dd3ad73c91727e4d0594f428acfa977206c20995612834497928d507eb62aca1752a8f3048c932b9f0f80f7c627a87f2b50d581961b8739bddfe2afabb1c757f366acd1e639de808409f598755dad254c60b5aefbbdcbad52f72c756e5e4b286a6866af769593f66256fadc939d3d23d1db9096038b40ed224ace023f2e3ea84fb4092c974cb44ffbe489f0ddbdd79e66281ef9c44e81781b849b0d3101c17e54ebf8bd69393b9220c75c7d3c564862ef35d7dfedc855e2ea15a6159c6c2bd01d2c4f3c316ddc43f937cc295fe35365a69ffe68a2a3bfa7eff90c2fe8563f6438117c31ab48cbd5a3ef1c7a03a03a048be4a9fe0de1d6a86feb144731f4e84f1b509db65d35b1b8ec3d0f462392da10694b207ef1d9fa2581b572f9c45012151f039ebed848b3fc211b2b4d6d48266e8bf800e68cb1165cfb17cb14af4fff107e57bc90b9e32006dd090ae12ff39b000c474f77da32549f51d07bb23d233485be9143c55849b5fa241337c050d48d88e4723f7f1032120cb609c584cb10cd777404556df84cd095c4a9668d392cb9a6197ce04e4234d48b47f8deaad83ee95292c9a9e9d42838c12e34046483ebd821284ac349fddb3d89c0e9a85716ca5f2c60569686d3580c6c7bce0a0ec4183fea724ad02763f66f85992fedf49c67a54c8ecc5b47d6e00cfeaf23b2425b795be93d65d92fe0ac761cca8b2feb4fd7a4bd21bc98a7328f178a61aabc2edf843e23ee94c757a457d448f3588b4e39cb14d855c35372c2060966df0e3382afe2d18988ee7676511e43afae09d6e16b50bfd290c1202c5c82520bfadb7b9eff22c2e9d202e7606f23182c08f0d405cfda6e8bf4b222a14a96015602cd77b2e0af5027938348075115b146166990bdccdaefa94626e140f8ea6fe6b51fb38fbf7ec39b89e68174db08d243a5da08a573545993db451bcd7462ba2c308849e6f54fd68eac003dff1971d19a00ae1d326d9db706197ce15397066ca114645ee39bb1a950c068908be503b2cf3ee74048dd92808e07172ba1362b3ad4103953c990e19b4581c54b5a240d90ec56150fdd5d9d1e497090941b541a9fa202d09f2790bd29f53fcf2adeddd4b4ecbff252921feca36cbe51e5185234641c8df314dec556280e408ad6605cb82f9fa5cbec32b2d478e876b4c3bc5019c344ee2f0bc33d26ae3b69e349771a8069f38f879d82e1c68f84d44516db921ca606b6e310e9ef0729b9fc76eaff94d3e44f865a6943eecc5ea1dc097e69e91344f7b287223fdf25ed3512e1fa34b0879ade1a2786571435e71d3fab19a6ba93b5d83e20f05afba10ab48ddee2c6feee813635318ac35bece3a339fb5c2278df5b9a6b7859343ff5530a2dbeda669a47a5eb0efc46c148ab00165563023536cf71f189c6b855ca6aaa056233ba82edf29e82d96c6118a0e6bf37d2ab2945ed1904f1dfd19ede3dfcf257aea6d560e3776159ffc384b3540deb1cc38d1022e530c2d46557a21eeb744ed5c00843f7b6d5953f1ff4770d26dde34c4cfbd308074e0df53264afc5a3a7ab8a57dae296c39bd72b88ad988319ba9e13ea529783d5c926d2f48599720695fd174f8873d0f660f002d8d0ee134271450c12e9dddb641b240795c2c09b958778e16081bc9180442c45fa916de16c83f16c50092eef58a56191bbcd906eb475b97d37b7f5cb00a79a9ad66a636e1052f9dd1e75d02a5af4840dfda7eac68c749bb857675e67b450a484d3e7b13a77fdabff0e97dfb705e5f4f6cf1e95a5f6cc38e099634a020087f868580ce2ec0837525b8c58f08444d7fd4333a589c0356de22568b4fad8766ee3325cbd65843f2c713ecdb44c96411ea871c039915b546ed6fbafbd51805ac48d06c6924d3f7036e1814250f50f27342c8c4ded3e68b6b3f161d46379c1088a7a123f48f0e7cb5a348f472eb155956fe232fd301e64f341041683ce3b25bba7f290a10282a8dba3a2a3da24461a5be148c2241d627889adca5acad981583fac81d0ee4ef77038c1f80db9dfe740720904512691a9c8545a9d173c08c2e8599010c972c2c34287d91ac7803a5700a0d6e29b7774f8f487b70cf8d0ec9474443e2c0c051116b16aef491c3945a65e6ddcd7931a7259e56902a2866b95d3c0bb7a3ea61b1f3b54ae56e6a7366ea895056ea0d1c251cd74f7b82b0d47464826f4aca77434df3d909271a825b57890cd830011981d95229cc0427cdc97758ddbc76d6cc77ba06c92d19daac8bbecbf55535e98bd4754ec06a6e632225c43bc46068baa688636eaba53926ca093a7addcd6a696a902ac35631aa43d9d66f77270cc7bf66140dac239034ba304e1aa0a265131e9fb2b7f079861b0e4cb9c911ce82ef0b685002476baf26401dc8cc444543129f82ac6b103881c596b19d9eba8ed6b230c17914d5c34a0040c18dc54d8c4b637ee683637fc5a82ac1cf12691bb28fc0bbb307fc032ec3d2b06eaec56ed769b5e892816c7350dce89551e87918f67a117c39f256a368586c78c2e9614e9658161511a8dad53afe8cb9eebe67c6596a90eeea1d3d2466a4d77a1129c0a4409b98d8ac0b925c4b2b3500665a3cf4ceb82cb0b6732eea8a796f9b79d2ea49be97066bc1f606d9f1f59f41d2acbb878a0783093fc4ab0ef866ff60a6a1a58d3cee90307f09247b5212f8709856251ff5d8fb77657110bbb3f3aeff07898f049c821a82c11e27b0c176a9feb12de5d08498018f7607156c5065cb56bf9d6867a4495f26a07e0f01312c2ee897b82d8eba0cbc473da402814dba727521cfec6afac2cc59cdd6a75e1f8f40585e5cda51a7434a81ccf4b7de33c663dc174ba973cebc5a56831005d231c719ea34ce42999c471fccfdbdbaf1acd2f9c16f258e32c70511c475ab264173246ebf31459a05ecb4df443066b61a243903e80ff907af17a96d7afd9763df8f8c4fc49775bc805e2dc165bd6f1c4e06688521557ed9ddb6860fbed1e32957bea1174b3a9aa809d7fa6301fbbb6b3774cd856095f14c6378cfe98f05d4f06fae91769165dd0adfc51bf8f57d701ef14a99d608db0a104ea78fe5b13794cb8529afa5352d1dbc8235d96148c8f9c2e29d6e2359a8dbeba56c9376b26f8384c66548979f4d982fe0652cd86bb60e6f2463ec63dcdd5f93d4bfaefe48f8012c63b32ad3c02ec9088896f6a0c8b1097c1ad911ada7a2d6f0d201a28b70752182885464dd688535bdfc045e8dafbe34b20eca00848e757b4a37de219be5a5fe7a4bc5cfaad29ed92e9eda2bed08407e0d0f53caf6b3590210067d8b9ef16f9a8f5612315dfa415f1efc8d7349394143a149480ce3ccd60ccaff0d9a8a797820f41b431ce3afc4adb2e07cde16015087e09e08bd13471dee960db35cbc3b53c187a5bca7ed50017e09b2ae2c837b1f6557753c7f5b004332ffa2b52d8a2269e7cf9cc397c6079aa5add61d7a560a894e71510e104f52a93622e34037b1db70a05bcfc546ea2ec7153e69a8df18fa9eadaae2c1438710477a9a23e0f7092c310c5288e2d39d362a0a33f9e3d8d9792b51a71d9014abcff66ee509baa3dad341b1e4b6c601a2966f77172a4df0f32170f3386a6600b0b63699fe21e26eeb475507e99f666e0ac349b9e23463450f4fa4498356887d9e1c5f7d18ade51e526d27ccae799d6775336ca9ca8e54d707639ecb0618a3c675533494e2435c0b3780a66defddd217d2cc464014bef8a051d8f292abf9e5cafa78c600c21ed3d40ede937b1e162a1e14757d39d77d4fad8711b6b46ae707b82ced0739f9fb6bcd9b557982e89bb3af5f3fb5448ea960f454f4475ee78970acda37501a8825a04cecf3e544651eea8933379da3c3e7de0a875d689003c00d276470fda3b6ed6473cf8094ab91784d1c0f9468379e8e9729dc1032a5ca14378f8147409f13cd6994de961e2245b35c814596087625d3d3267fc0c1e5614a4af94993091ead40bc9e1d3093228b70c188855ae9e914b15aacfd4f83fde83072af92b2cc968c93cac74e15322eaff32a7bbbb982fb725aeb71f34bf16323d9c0a11dbaf3ab676a9cd1dcfc3f8a0c66d1f082f23806133002c50b59d4513dbe3419d5002263287ff47abdba0862341effe669f26b375337170c8e0742113e1063e8141c4aa9eb4970471f3187f581b71e6f7fe2f8043d065620da8a066d112fedeb33525eb1061c0d0fe9fb415bddae8ed2eb5c3ae6aa0549230e436afacaddc389b2c66499d7fdec2090e7e13560ca0a64803554c7cd9cfcc1cb48427cf9ccd954bb7446c887e2756db2882ff12eaa64efae3a24b35d1d0402922efe90319510495420301d3360f4486d3f87e3dc4f9337bf3fb4e3c6a82850a840153a1936e7cf74086757b72a8db19d33a62a29f3dd4fdef454d9222031aa0958af21851b66aebc09a5c08efd204f3ff18cb1055e8181d6630309fcc91c0d6daef19e618a3ee23e817a586d02364710cfab0b9f2cf18502a34e67d112f1730d44ccae54dc221d7f3877bb828e7109878109f8e95e2e1407df4e588801d25d9c2a1c501e74890631e9a92d823ebbe6b5635488f7d48788ef77658e3bbaf287536b37d3a7ab1ec1749656f2ebfe562765e71dd3e1b895d9b5c315fcf2b3a063c57e74ad1e7586b293ede4c77732f38d316c14210a121153fc50007f78ed64a8e207e9d04b312ae7f97a946c74d2a1181b67e845c3ac6e340b2428c8a5546679707fded3406fc221900b118a3279e13b74926c793e27fc4cc32ae478b4421d6eef75d3a273ff61d0e95b4981e8dd57e16bb00e09bfbbc2ce60cd844a9abb839b8b671fabddfd6e86a30c0a24e73c3c17770f34641951e5dc73ca11d8f8419a7407d483e0f5f1714df0a1775574b5500e8a5a28c655dbc28d7a1ca4b83fd4ebcc7ef2e4994c97c87659681acebe7417328c8612e8570e7ade7ead7f4fc711c9c539362779e6be525bdf5ec037f670b5235c06a1acd89b4ffc21668a7269cc73bf6d1399852eebb8b1dde8ef072e8d80832ba32c8e9480da2c4f5c3209c557f31beef41c00d22ee7c7e2c1bf9952ba8a03c1afae9b4aa63135d2b131f2b2804afcdcc762e1bcec8c8151f471572888933ce97dd787121ced446aa9718bf3766bb6d8a752692c59489d5b565e1693aa0f67b352f915808e415cba13a9864bbd33ebc97dfdc0d357d6769f2f545cc6529c0f634da901ae63bfcbab0a3896bc43faed6a6c23bb4e92f3d669d2e0ff485287cce322b98d02866f026cc556ec8aba6608ac2b5dbc29e104ef2e28d7b51ce63110025bdbfc5d44e8aa7a04ecece07b9860618a162e7289e8d672bb9b15b6ffc87f738b0c7a2b733c5794afe58b1beee4b6780ed453bf2ef2b584dcf32bf732c98fe359abced05fc115e531b088c61b0d5d5058af10120581d7db192e13a5b7b17874f000343aecc8d5005b91b13720bc831de5f1de5e3ddce27ba05213cd126a7cda0afa9745f498200269a5736f63b0faec36bbd646a868100c17cb7f6639f2f14b6c52198fab04c1645bed8763799acf8fef62b82fda1825a3379c000255002788d686695b4c17be3931e69db8980d0216024e9b7b0588cdf8c8102d11f55f971b3163c392cfaa796e0b85dd0bbacd6ca50b3ab80c2e90fa0c18d3526e05b2a46c2eab823c0511b43c71122d533e27ee6d6e34706fc411c67a3b87440a3429df3009996743ed3e4dc244fac98a789f17818a926a0aae81ecde260982b80acc299f57a570a86ee28d0414edc91fb6d5f9a88aeb31bf22270bf3517aefe1140b05be97123cc43df6e8e8e4df96803fdd59715c87afcf0189fb5448663eb35d2c4e5b13dd0233a95f8d6187bf0d5d3ba35adba59e162e877d5a0397d9495ebfc771ae68283be15d883e91b81b1bb0cd8da6c300df7e2bc8a21094cadc974c8270d8ee37fc7e7501a57eaecbc244ed61cfc8d556e38c0611a5269c3b930ee5f37a9771f0c152a5e28df07a104360c973b9a83d3ec5c0aa012bff141842e9b68222647c7d022753dbaae024877f421ff36b3721c26a39b3009683c8c510ba0ba8b5dc1033f9b56e9a43b3141a92599378622a2ca8136f5f1f51cf7b7dce7d043f65f8562b33c4864adc30e7d4c808b10abbbd92f94272b68b063f7d7baf7fd6eb31cc76690042233bc8dee7253f89ce23de7a535af022dae95ac321694d6ce311744d9c152e4424a0a502d221b2e602ada71c60a2f15b7086d75867476b0633063297681fbb0a3e154efe552cdbd9d3203f2e447b60b643b823ea12f504f33f6b6c3bd20e54cf38e3c45c5d472814db60741687894e6cc3c78196d5e722499d202334fb742f14dc2ccb7d114ae0c4cd61ce2ed0cc7fe25a395d6b73c1dfee9174e59d129e7f3c42f93a246d918028d4e2dc804438799 +").unwrap(); let signature = manager.sign_transaction(&address, &huge_tx); println!("Got {:?}", signature); assert!(signature.is_ok()); diff --git a/hw/src/lib.rs b/hw/src/lib.rs index 9bfec83410b55300551f6acb448bde7a85e45471..98e682a305bcb6e35970b6fae547e1b5782f387e 100644 --- a/hw/src/lib.rs +++ b/hw/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -17,6 +17,7 @@ //! Hardware wallet management. #![warn(missing_docs)] +#![warn(warnings)] extern crate ethereum_types; extern crate ethkey; @@ -24,25 +25,75 @@ extern crate hidapi; extern crate libusb; extern crate parking_lot; extern crate protobuf; +extern crate semver; extern crate trezor_sys; + #[macro_use] extern crate log; #[cfg(test)] extern crate rustc_hex; mod ledger; mod trezor; -use ethkey::{Address, Signature}; +use std::sync::{Arc, atomic, atomic::AtomicBool}; +use std::{fmt, time::Duration}; -use parking_lot::Mutex; -use std::fmt; -use std::sync::Arc; -use std::sync::atomic; -use std::sync::atomic::AtomicBool; -use std::thread; -use std::time::Duration; use ethereum_types::U256; +use ethkey::{Address, Signature}; +use parking_lot::Mutex; const USB_DEVICE_CLASS_DEVICE: u8 = 0; +const POLLING_DURATION: Duration = Duration::from_millis(500); + +/// `HardwareWallet` device +#[derive(Debug)] +pub struct Device { + path: String, + info: WalletInfo, +} + +/// `Wallet` trait +pub trait Wallet<'a> { + /// Error + type Error; + /// Transaction data format + type Transaction; + + /// Sign transaction data with wallet managing `address`. + fn sign_transaction(&self, address: &Address, transaction: Self::Transaction) -> Result; + + /// Set key derivation path for a chain. + fn set_key_path(&self, key_path: KeyPath); + + /// Re-populate device list + /// Note, this assumes all devices are iterated over and updated + fn update_devices(&self, device_direction: DeviceDirection) -> Result; + + /// Read device info + fn read_device(&self, usb: &hidapi::HidApi, dev_info: &hidapi::HidDeviceInfo) -> Result; + + /// List connected and acknowledged wallets + fn list_devices(&self) -> Vec; + + /// List locked wallets + /// This may be moved if it is the wrong assumption, for example this is not supported by Ledger + /// Then this method return a empty vector + fn list_locked_devices(&self) -> Vec; + + /// Get wallet info. + fn get_wallet(&self, address: &Address) -> Option; + + /// Generate ethereum address for a Wallet + fn get_address(&self, device: &hidapi::HidDevice) -> Result, Self::Error>; + + /// Open a device using `device path` + /// Note, f - is a closure that borrows HidResult + /// HidDevice is in turn a type alias for a `c_void function pointer` + /// For further information see: + /// * + /// * + fn open_path(&self, f: F) -> Result + where F: Fn() -> Result; +} /// Hardware wallet error. #[derive(Debug)] @@ -60,7 +111,7 @@ pub enum Error { } /// This is the transaction info we need to supply to Trezor message. It's more -/// or less a duplicate of ethcore::transaction::Transaction, but we can't +/// or less a duplicate of `ethcore::transaction::Transaction`, but we can't /// import ethcore here as that would be a circular dependency. pub struct TransactionInfo { /// Nonce @@ -114,7 +165,7 @@ impl fmt::Display for Error { } impl From for Error { - fn from(err: ledger::Error) -> Error { + fn from(err: ledger::Error) -> Self { match err { ledger::Error::KeyNotFound => Error::KeyNotFound, _ => Error::LedgerDevice(err), @@ -123,7 +174,7 @@ impl From for Error { } impl From for Error { - fn from(err: trezor::Error) -> Error { + fn from(err: trezor::Error) -> Self { match err { trezor::Error::KeyNotFound => Error::KeyNotFound, _ => Error::TrezorDevice(err), @@ -132,11 +183,29 @@ impl From for Error { } impl From for Error { - fn from(err: libusb::Error) -> Error { + fn from(err: libusb::Error) -> Self { Error::Usb(err) } } +/// Specifies the direction of the `HardwareWallet` i.e, whether it arrived or left +#[derive(Debug, Copy, Clone)] +pub enum DeviceDirection { + /// Device arrived + Arrived, + /// Device left + Left, +} + +impl fmt::Display for DeviceDirection { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + DeviceDirection::Arrived => write!(f, "arrived"), + DeviceDirection::Left => write!(f, "left"), + } + } +} + /// Hardware wallet management interface. pub struct HardwareWalletManager { exiting: Arc, @@ -144,79 +213,24 @@ pub struct HardwareWalletManager { trezor: Arc, } - impl HardwareWalletManager { /// Hardware wallet constructor - pub fn new() -> Result { - let usb_context_trezor = Arc::new(libusb::Context::new()?); - let usb_context_ledger = Arc::new(libusb::Context::new()?); + pub fn new() -> Result { + let exiting = Arc::new(AtomicBool::new(false)); let hidapi = Arc::new(Mutex::new(hidapi::HidApi::new().map_err(|e| Error::Hid(e.to_string().clone()))?)); - let ledger = Arc::new(ledger::Manager::new(hidapi.clone())); - let trezor = Arc::new(trezor::Manager::new(hidapi.clone())); - - // Subscribe to TREZOR V1 - // Note, this support only TREZOR V1 becasue TREZOR V2 has another vendorID for some reason - // Also, we now only support one product as the second argument specifies - usb_context_trezor.register_callback( - Some(trezor::TREZOR_VID), Some(trezor::TREZOR_PIDS[0]), Some(USB_DEVICE_CLASS_DEVICE), - Box::new(trezor::EventHandler::new(Arc::downgrade(&trezor))))?; - - // Subscribe to all Ledger Devices - // This means that we need to check that the given productID is supported - // None => LIBUSB_HOTPLUG_MATCH_ANY, in other words that all are subscribed to - // More info can be found: http://libusb.sourceforge.net/api-1.0/group__hotplug.html#gae6c5f1add6cc754005549c7259dc35ea - usb_context_ledger.register_callback( - Some(ledger::LEDGER_VID), None, Some(USB_DEVICE_CLASS_DEVICE), - Box::new(ledger::EventHandler::new(Arc::downgrade(&ledger))))?; + let ledger = ledger::Manager::new(hidapi.clone(), exiting.clone())?; + let trezor = trezor::Manager::new(hidapi.clone(), exiting.clone())?; - let exiting = Arc::new(AtomicBool::new(false)); - let thread_exiting_ledger = exiting.clone(); - let thread_exiting_trezor = exiting.clone(); - let l = ledger.clone(); - let t = trezor.clone(); - - // Ledger event thread - thread::Builder::new() - .name("hw_wallet_ledger".to_string()) - .spawn(move || { - if let Err(e) = l.update_devices() { - debug!(target: "hw", "Ledger couldn't connect at startup, error: {}", e); - } - loop { - usb_context_ledger.handle_events(Some(Duration::from_millis(500))) - .unwrap_or_else(|e| debug!(target: "hw", "Ledger event handler error: {}", e)); - if thread_exiting_ledger.load(atomic::Ordering::Acquire) { - break; - } - } - }) - .ok(); - - // Trezor event thread - thread::Builder::new() - .name("hw_wallet_trezor".to_string()) - .spawn(move || { - if let Err(e) = t.update_devices() { - debug!(target: "hw", "Trezor couldn't connect at startup, error: {}", e); - } - loop { - usb_context_trezor.handle_events(Some(Duration::from_millis(500))) - .unwrap_or_else(|e| debug!(target: "hw", "Trezor event handler error: {}", e)); - if thread_exiting_trezor.load(atomic::Ordering::Acquire) { - break; - } - } - }) - .ok(); - - Ok(HardwareWalletManager { - exiting: exiting, - ledger: ledger, - trezor: trezor, + Ok(Self { + exiting, + ledger, + trezor, }) } /// Select key derivation path for a chain. + /// Currently, only one hard-coded keypath is supported + /// It is managed by `ethcore/account_provider` pub fn set_key_path(&self, key_path: KeyPath) { self.ledger.set_key_path(key_path); self.trezor.set_key_path(key_path); @@ -231,24 +245,37 @@ impl HardwareWalletManager { } /// Return a list of paths to locked hardware wallets + /// This is only applicable to Trezor because Ledger only appears as + /// a device when it is unlocked pub fn list_locked_wallets(&self) -> Result, Error> { Ok(self.trezor.list_locked_devices()) } /// Get connected wallet info. pub fn wallet_info(&self, address: &Address) -> Option { - if let Some(info) = self.ledger.device_info(address) { + if let Some(info) = self.ledger.get_wallet(address) { Some(info) } else { - self.trezor.device_info(address) + self.trezor.get_wallet(address) + } + } + + /// Sign a message with the wallet (only supported by Ledger) + pub fn sign_message(&self, address: &Address, msg: &[u8]) -> Result { + if self.ledger.get_wallet(address).is_some() { + Ok(self.ledger.sign_message(address, msg)?) + } else if self.trezor.get_wallet(address).is_some() { + Err(Error::TrezorDevice(trezor::Error::NoSigningMessage)) + } else { + Err(Error::KeyNotFound) } } /// Sign transaction data with wallet managing `address`. pub fn sign_transaction(&self, address: &Address, t_info: &TransactionInfo, encoded_transaction: &[u8]) -> Result { - if self.ledger.device_info(address).is_some() { + if self.ledger.get_wallet(address).is_some() { Ok(self.ledger.sign_transaction(address, encoded_transaction)?) - } else if self.trezor.device_info(address).is_some() { + } else if self.trezor.get_wallet(address).is_some() { Ok(self.trezor.sign_transaction(address, t_info)?) } else { Err(Error::KeyNotFound) @@ -256,6 +283,8 @@ impl HardwareWalletManager { } /// Send a pin to a device at a certain path to unlock it + /// This is only applicable to Trezor because Ledger only appears as + /// a device when it is unlocked pub fn pin_matrix_ack(&self, path: &str, pin: &str) -> Result { self.trezor.pin_matrix_ack(path, pin).map_err(Error::TrezorDevice) } diff --git a/hw/src/trezor.rs b/hw/src/trezor.rs index 7db226718bc6681981cfea05a22ae5e93390be70..efd259d7806f456a8bbbb04b6ebc10f1759b9b01 100644 --- a/hw/src/trezor.rs +++ b/hw/src/trezor.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -15,34 +15,31 @@ // along with Parity. If not, see . //! Trezor hardware wallet module. Supports Trezor v1. -//! See http://doc.satoshilabs.com/trezor-tech/api-protobuf.html -//! and https://github.com/trezor/trezor-common/blob/master/protob/protocol.md +//! See +//! and //! for protocol details. -use super::{WalletInfo, TransactionInfo, KeyPath}; - use std::cmp::{min, max}; -use std::fmt; -use std::sync::{Arc, Weak}; +use std::sync::{atomic, atomic::AtomicBool, Arc, Weak}; use std::time::{Duration, Instant}; +use std::{fmt, thread}; use ethereum_types::{U256, H256, Address}; use ethkey::Signature; use hidapi; use libusb; use parking_lot::{Mutex, RwLock}; -use protobuf; -use protobuf::{Message, ProtobufEnum}; - +use protobuf::{self, Message, ProtobufEnum}; +use super::{DeviceDirection, WalletInfo, TransactionInfo, KeyPath, Wallet, Device, USB_DEVICE_CLASS_DEVICE, POLLING_DURATION}; use trezor_sys::messages::{EthereumAddress, PinMatrixAck, MessageType, EthereumTxRequest, EthereumSignTx, EthereumGetAddress, EthereumTxAck, ButtonAck}; /// Trezor v1 vendor ID -pub const TREZOR_VID: u16 = 0x534c; +const TREZOR_VID: u16 = 0x534c; /// Trezor product IDs -pub const TREZOR_PIDS: [u16; 1] = [0x0001]; +const TREZOR_PIDS: [u16; 1] = [0x0001]; -const ETH_DERIVATION_PATH: [u32; 5] = [0x8000002C, 0x8000003C, 0x80000000, 0, 0]; // m/44'/60'/0'/0/0 -const ETC_DERIVATION_PATH: [u32; 5] = [0x8000002C, 0x8000003D, 0x80000000, 0, 0]; // m/44'/61'/0'/0/0 +const ETH_DERIVATION_PATH: [u32; 5] = [0x8000_002C, 0x8000_003C, 0x8000_0000, 0, 0]; // m/44'/60'/0'/0/0 +const ETC_DERIVATION_PATH: [u32; 5] = [0x8000_002C, 0x8000_003D, 0x8000_0000, 0, 0]; // m/44'/61'/0'/0/0 /// Hardware wallet error. #[derive(Debug)] @@ -59,6 +56,12 @@ pub enum Error { BadMessageType, /// Trying to read from a closed device at the given path LockedDevice(String), + /// Signing messages are not supported by Trezor + NoSigningMessage, + /// No device arrived + NoDeviceArrived, + /// No device left + NoDeviceLeft, } impl fmt::Display for Error { @@ -70,36 +73,33 @@ impl fmt::Display for Error { Error::UserCancel => write!(f, "Operation has been cancelled"), Error::BadMessageType => write!(f, "Bad Message Type in RPC call"), Error::LockedDevice(ref s) => write!(f, "Device is locked, needs PIN to perform operations: {}", s), + Error::NoSigningMessage=> write!(f, "Signing messages are not supported by Trezor"), + Error::NoDeviceArrived => write!(f, "No device arrived"), + Error::NoDeviceLeft=> write!(f, "No device left"), } } } impl From for Error { - fn from(err: hidapi::HidError) -> Error { + fn from(err: hidapi::HidError) -> Self { Error::Usb(err) } } impl From for Error { - fn from(_: protobuf::ProtobufError) -> Error { + fn from(_: protobuf::ProtobufError) -> Self { Error::Protocol(&"Could not read response from Trezor Device") } } -/// Ledger device manager -pub struct Manager { +/// Trezor device manager +pub (crate) struct Manager { usb: Arc>, devices: RwLock>, locked_devices: RwLock>, key_path: RwLock, } -#[derive(Debug)] -struct Device { - path: String, - info: WalletInfo, -} - /// HID Version used for the Trezor device enum HidVersion { V1, @@ -108,102 +108,42 @@ enum HidVersion { impl Manager { /// Create a new instance. - pub fn new(hidapi: Arc>) -> Manager { - Manager { + pub fn new(hidapi: Arc>, exiting: Arc) -> Result, libusb::Error> { + let manager = Arc::new(Self { usb: hidapi, devices: RwLock::new(Vec::new()), locked_devices: RwLock::new(Vec::new()), key_path: RwLock::new(KeyPath::Ethereum), - } - } - - /// Re-populate device list - pub fn update_devices(&self) -> Result { - let mut usb = self.usb.lock(); - usb.refresh_devices(); - let devices = usb.devices(); - let mut new_devices = Vec::new(); - let mut locked_devices = Vec::new(); - let mut error = None; - for usb_device in devices { - let is_trezor = usb_device.vendor_id == TREZOR_VID; - let is_supported_product = TREZOR_PIDS.contains(&usb_device.product_id); - let is_valid = usb_device.usage_page == 0xFF00 || usb_device.interface_number == 0; - - trace!( - "Checking device: {:?}, trezor: {:?}, prod: {:?}, valid: {:?}", - usb_device, - is_trezor, - is_supported_product, - is_valid, - ); - if !is_trezor || !is_supported_product || !is_valid { - continue; - } - match self.read_device_info(&usb, &usb_device) { - Ok(device) => new_devices.push(device), - Err(Error::LockedDevice(path)) => locked_devices.push(path.to_string()), - Err(e) => { - warn!("Error reading device: {:?}", e); - error = Some(e); + }); + + let usb_context = Arc::new(libusb::Context::new()?); + let m = manager.clone(); + + // Subscribe to TREZOR V1 + // Note, this support only TREZOR V1 because TREZOR V2 has a different vendorID for some reason + // Also, we now only support one product as the second argument specifies + usb_context.register_callback( + Some(TREZOR_VID), Some(TREZOR_PIDS[0]), Some(USB_DEVICE_CLASS_DEVICE), + Box::new(EventHandler::new(Arc::downgrade(&manager))))?; + + // Trezor event thread + thread::Builder::new() + .name("hw_wallet_trezor".to_string()) + .spawn(move || { + if let Err(e) = m.update_devices(DeviceDirection::Arrived) { + debug!(target: "hw", "Trezor couldn't connect at startup, error: {}", e); } - } - } - let count = new_devices.len(); - trace!("Got devices: {:?}, closed: {:?}", new_devices, locked_devices); - *self.devices.write() = new_devices; - *self.locked_devices.write() = locked_devices; - match error { - Some(e) => Err(e), - None => Ok(count), - } - } - - fn read_device_info(&self, usb: &hidapi::HidApi, dev_info: &hidapi::HidDeviceInfo) -> Result { - let handle = self.open_path(|| usb.open_path(&dev_info.path))?; - let manufacturer = dev_info.manufacturer_string.clone().unwrap_or("Unknown".to_owned()); - let name = dev_info.product_string.clone().unwrap_or("Unknown".to_owned()); - let serial = dev_info.serial_number.clone().unwrap_or("Unknown".to_owned()); - match self.get_address(&handle) { - Ok(Some(addr)) => { - Ok(Device { - path: dev_info.path.clone(), - info: WalletInfo { - name: name, - manufacturer: manufacturer, - serial: serial, - address: addr, - }, - }) - } - Ok(None) => Err(Error::LockedDevice(dev_info.path.clone())), - Err(e) => Err(e), - } - } - - /// Select key derivation path for a known chain. - pub fn set_key_path(&self, key_path: KeyPath) { - *self.key_path.write() = key_path; - } - - /// List connected wallets. This only returns wallets that are ready to be used. - pub fn list_devices(&self) -> Vec { - self.devices.read().iter().map(|d| d.info.clone()).collect() - } - - pub fn list_locked_devices(&self) -> Vec { - (*self.locked_devices.read()).clone() - } - - /// Get wallet info. - pub fn device_info(&self, address: &Address) -> Option { - self.devices.read().iter().find(|d| &d.info.address == address).map(|d| d.info.clone()) - } + loop { + usb_context.handle_events(Some(Duration::from_millis(500))) + .unwrap_or_else(|e| debug!(target: "hw", "Trezor event handler error: {}", e)); + if exiting.load(atomic::Ordering::Acquire) { + break; + } + } + }) + .ok(); - fn open_path(&self, f: F) -> Result - where F: Fn() -> Result - { - f().map_err(Into::into) + Ok(manager) } pub fn pin_matrix_ack(&self, device_path: &str, pin: &str) -> Result { @@ -223,68 +163,12 @@ impl Manager { } }; - self.update_devices()?; + self.update_devices(DeviceDirection::Arrived)?; unlocked } - fn get_address(&self, device: &hidapi::HidDevice) -> Result, Error> { - let typ = MessageType::MessageType_EthereumGetAddress; - let mut message = EthereumGetAddress::new(); - match *self.key_path.read() { - KeyPath::Ethereum => message.set_address_n(ETH_DERIVATION_PATH.to_vec()), - KeyPath::EthereumClassic => message.set_address_n(ETC_DERIVATION_PATH.to_vec()), - } - message.set_show_display(false); - self.send_device_message(&device, &typ, &message)?; - - let (resp_type, bytes) = self.read_device_response(&device)?; - match resp_type { - MessageType::MessageType_EthereumAddress => { - let response: EthereumAddress = protobuf::core::parse_from_bytes(&bytes)?; - Ok(Some(From::from(response.get_address()))) - } - _ => Ok(None), - } - } - - /// Sign transaction data with wallet managing `address`. - pub fn sign_transaction(&self, address: &Address, t_info: &TransactionInfo) -> Result { - let usb = self.usb.lock(); - let devices = self.devices.read(); - let device = devices.iter().find(|d| &d.info.address == address).ok_or(Error::KeyNotFound)?; - let handle = self.open_path(|| usb.open_path(&device.path))?; - let msg_type = MessageType::MessageType_EthereumSignTx; - let mut message = EthereumSignTx::new(); - match *self.key_path.read() { - KeyPath::Ethereum => message.set_address_n(ETH_DERIVATION_PATH.to_vec()), - KeyPath::EthereumClassic => message.set_address_n(ETC_DERIVATION_PATH.to_vec()), - } - message.set_nonce(self.u256_to_be_vec(&t_info.nonce)); - message.set_gas_limit(self.u256_to_be_vec(&t_info.gas_limit)); - message.set_gas_price(self.u256_to_be_vec(&t_info.gas_price)); - message.set_value(self.u256_to_be_vec(&t_info.value)); - - match t_info.to { - Some(addr) => { - message.set_to(addr.to_vec()) - } - None => (), - } - let first_chunk_length = min(t_info.data.len(), 1024); - let chunk = &t_info.data[0..first_chunk_length]; - message.set_data_initial_chunk(chunk.to_vec()); - message.set_data_length(t_info.data.len() as u32); - if let Some(c_id) = t_info.chain_id { - message.set_chain_id(c_id as u32); - } - - self.send_device_message(&handle, &msg_type, &message)?; - - self.signing_loop(&handle, &t_info.chain_id, &t_info.data[first_chunk_length..]) - } - fn u256_to_be_vec(&self, val: &U256) -> Vec { - let mut buf = [0u8; 32]; + let mut buf = [0_u8; 32]; val.to_big_endian(&mut buf); buf.iter().skip_while(|x| **x == 0).cloned().collect() } @@ -339,8 +223,8 @@ impl Manager { let mut data = Vec::new(); let hid_version = self.probe_hid_version(device)?; // Magic constants - data.push('#' as u8); - data.push('#' as u8); + data.push(b'#'); + data.push(b'#'); // Convert msg_id to BE and split into bytes data.push(((msg_id >> 8) & 0xFF) as u8); data.push((msg_id & 0xFF) as u8); @@ -356,8 +240,8 @@ impl Manager { let mut total_written = 0; for chunk in data.chunks(63) { let mut padded_chunk = match hid_version { - HidVersion::V1 => vec!['?' as u8], - HidVersion::V2 => vec![0, '?' as u8], + HidVersion::V1 => vec![b'?'], + HidVersion::V2 => vec![0, b'?'], }; padded_chunk.extend_from_slice(&chunk); total_written += device.write(&padded_chunk)?; @@ -366,10 +250,10 @@ impl Manager { } fn probe_hid_version(&self, device: &hidapi::HidDevice) -> Result { - let mut buf2 = [0xFFu8; 65]; + let mut buf2 = [0xFF_u8; 65]; buf2[0] = 0; buf2[1] = 63; - let mut buf1 = [0xFFu8; 64]; + let mut buf1 = [0xFF_u8; 64]; buf1[0] = 63; if device.write(&buf2)? == 65 { Ok(HidVersion::V2) @@ -385,7 +269,7 @@ impl Manager { let mut buf = vec![0; 64]; let first_chunk = device.read_timeout(&mut buf, 300_000)?; - if first_chunk < 9 || buf[0] != '?' as u8 || buf[1] != '#' as u8 || buf[2] != '#' as u8 { + if first_chunk < 9 || buf[0] != b'?' || buf[1] != b'#' || buf[2] != b'#' { return Err(protocol_err); } let msg_type = MessageType::from_i32(((buf[3] as i32 & 0xFF) << 8) + (buf[4] as i32 & 0xFF)).ok_or(protocol_err)?; @@ -400,11 +284,160 @@ impl Manager { } } +impl <'a>Wallet<'a> for Manager { + type Error = Error; + type Transaction = &'a TransactionInfo; + + fn sign_transaction(&self, address: &Address, t_info: Self::Transaction) -> + Result { + let usb = self.usb.lock(); + let devices = self.devices.read(); + let device = devices.iter().find(|d| &d.info.address == address).ok_or(Error::KeyNotFound)?; + let handle = self.open_path(|| usb.open_path(&device.path))?; + let msg_type = MessageType::MessageType_EthereumSignTx; + let mut message = EthereumSignTx::new(); + match *self.key_path.read() { + KeyPath::Ethereum => message.set_address_n(ETH_DERIVATION_PATH.to_vec()), + KeyPath::EthereumClassic => message.set_address_n(ETC_DERIVATION_PATH.to_vec()), + } + message.set_nonce(self.u256_to_be_vec(&t_info.nonce)); + message.set_gas_limit(self.u256_to_be_vec(&t_info.gas_limit)); + message.set_gas_price(self.u256_to_be_vec(&t_info.gas_price)); + message.set_value(self.u256_to_be_vec(&t_info.value)); + + if let Some(addr) = t_info.to { + message.set_to(addr.to_vec()) + } + let first_chunk_length = min(t_info.data.len(), 1024); + let chunk = &t_info.data[0..first_chunk_length]; + message.set_data_initial_chunk(chunk.to_vec()); + message.set_data_length(t_info.data.len() as u32); + if let Some(c_id) = t_info.chain_id { + message.set_chain_id(c_id as u32); + } + + self.send_device_message(&handle, &msg_type, &message)?; + + self.signing_loop(&handle, &t_info.chain_id, &t_info.data[first_chunk_length..]) + } + + fn set_key_path(&self, key_path: KeyPath) { + *self.key_path.write() = key_path; + } + + fn update_devices(&self, device_direction: DeviceDirection) -> Result { + let mut usb = self.usb.lock(); + usb.refresh_devices(); + let devices = usb.devices(); + let num_prev_devices = self.devices.read().len(); + + let detected_devices = devices.iter() + .filter(|&d| { + let is_trezor = d.vendor_id == TREZOR_VID; + let is_supported_product = TREZOR_PIDS.contains(&d.product_id); + let is_valid = d.usage_page == 0xFF00 || d.interface_number == 0; + + is_trezor && is_supported_product && is_valid + }) + .fold(Vec::new(), |mut v, d| { + match self.read_device(&usb, &d) { + Ok(info) => { + trace!(target: "hw", "Found device: {:?}", info); + v.push(info); + } + Err(e) => trace!(target: "hw", "Error reading device info: {}", e), + }; + v + }); + + let num_curr_devices = detected_devices.len(); + *self.devices.write() = detected_devices; + + match device_direction { + DeviceDirection::Arrived => { + if num_curr_devices > num_prev_devices { + Ok(num_curr_devices - num_prev_devices) + } else { + Err(Error::NoDeviceArrived) + } + } + DeviceDirection::Left => { + if num_prev_devices > num_curr_devices { + Ok(num_prev_devices- num_curr_devices) + } else { + Err(Error::NoDeviceLeft) + } + } + } + } + + fn read_device(&self, usb: &hidapi::HidApi, dev_info: &hidapi::HidDeviceInfo) -> Result { + let handle = self.open_path(|| usb.open_path(&dev_info.path))?; + let manufacturer = dev_info.manufacturer_string.clone().unwrap_or_else(|| "Unknown".to_owned()); + let name = dev_info.product_string.clone().unwrap_or_else(|| "Unknown".to_owned()); + let serial = dev_info.serial_number.clone().unwrap_or_else(|| "Unknown".to_owned()); + match self.get_address(&handle) { + Ok(Some(addr)) => { + Ok(Device { + path: dev_info.path.clone(), + info: WalletInfo { + name, + manufacturer, + serial, + address: addr, + }, + }) + } + Ok(None) => Err(Error::LockedDevice(dev_info.path.clone())), + Err(e) => Err(e), + } + } + + fn list_devices(&self) -> Vec { + self.devices.read().iter().map(|d| d.info.clone()).collect() + } + + fn list_locked_devices(&self) -> Vec { + (*self.locked_devices.read()).clone() + } + + fn get_wallet(&self, address: &Address) -> Option { + self.devices.read().iter().find(|d| &d.info.address == address).map(|d| d.info.clone()) + } + + fn get_address(&self, device: &hidapi::HidDevice) -> Result, Error> { + let typ = MessageType::MessageType_EthereumGetAddress; + let mut message = EthereumGetAddress::new(); + match *self.key_path.read() { + KeyPath::Ethereum => message.set_address_n(ETH_DERIVATION_PATH.to_vec()), + KeyPath::EthereumClassic => message.set_address_n(ETC_DERIVATION_PATH.to_vec()), + } + message.set_show_display(false); + self.send_device_message(&device, &typ, &message)?; + + let (resp_type, bytes) = self.read_device_response(&device)?; + match resp_type { + MessageType::MessageType_EthereumAddress => { + let response: EthereumAddress = protobuf::core::parse_from_bytes(&bytes)?; + Ok(Some(From::from(response.get_address()))) + } + _ => Ok(None), + } + } + + fn open_path(&self, f: F) -> Result + where F: Fn() -> Result + { + f().map_err(Into::into) + } +} + // Try to connect to the device using polling in at most the time specified by the `timeout` -fn try_connect_polling(trezor: Arc, duration: Duration) -> bool { +fn try_connect_polling(trezor: &Manager, duration: &Duration, dir: DeviceDirection) -> bool { let start_time = Instant::now(); - while start_time.elapsed() <= duration { - if let Ok(_) = trezor.update_devices() { + while start_time.elapsed() <= *duration { + if let Ok(num_devices) = trezor.update_devices(dir) { + trace!(target: "hw", "{} Trezor devices {}", num_devices, dir); return true } } @@ -412,18 +445,18 @@ fn try_connect_polling(trezor: Arc, duration: Duration) -> bool { } /// Trezor event handler -/// A separate thread is handeling incoming events +/// A separate thread is handling incoming events /// /// Note, that this run to completion and race-conditions can't occur but this can /// therefore starve other events for being process with a spinlock or similar -pub struct EventHandler { +struct EventHandler { trezor: Weak, } impl EventHandler { /// Trezor event handler constructor pub fn new(trezor: Weak) -> Self { - Self { trezor: trezor } + Self { trezor } } } @@ -431,8 +464,8 @@ impl libusb::Hotplug for EventHandler { fn device_arrived(&mut self, _device: libusb::Device) { debug!(target: "hw", "Trezor V1 arrived"); if let Some(trezor) = self.trezor.upgrade() { - if try_connect_polling(trezor, Duration::from_millis(500)) != true { - debug!(target: "hw", "Ledger connect timeout"); + if try_connect_polling(&trezor, &POLLING_DURATION, DeviceDirection::Arrived) != true { + trace!(target: "hw", "No Trezor connected"); } } } @@ -440,8 +473,8 @@ impl libusb::Hotplug for EventHandler { fn device_left(&mut self, _device: libusb::Device) { debug!(target: "hw", "Trezor V1 left"); if let Some(trezor) = self.trezor.upgrade() { - if try_connect_polling(trezor, Duration::from_millis(500)) != true { - debug!(target: "hw", "Ledger disconnect timeout"); + if try_connect_polling(&trezor, &POLLING_DURATION, DeviceDirection::Left) != true { + trace!(target: "hw", "No Trezor disconnected"); } } } @@ -454,11 +487,14 @@ impl libusb::Hotplug for EventHandler { fn test_signature() { use ethereum_types::{H160, H256, U256}; - let hidapi = Arc::new(Mutex::new(hidapi::HidApi::new().unwrap())); - let manager = Manager::new(hidapi.clone()); + let manager = Manager::new( + Arc::new(Mutex::new(hidapi::HidApi::new().expect("HidApi"))), + Arc::new(AtomicBool::new(false)) + ).expect("HardwareWalletManager"); + let addr: Address = H160::from("some_addr"); - manager.update_devices().unwrap(); + assert_eq!(try_connect_polling(&manager.clone(), &POLLING_DURATION, DeviceDirection::Arrived), true); let t_info = TransactionInfo { nonce: U256::from(1), diff --git a/ipfs/Cargo.toml b/ipfs/Cargo.toml index d8e3ef2a95bd9b4be9d51ff562a501a9fb1b40fb..5a72048134cbcc3671cb5f8376825b24db70b4e1 100644 --- a/ipfs/Cargo.toml +++ b/ipfs/Cargo.toml @@ -1,17 +1,20 @@ [package] description = "Parity IPFS-compatible API" name = "parity-ipfs-api" -version = "1.11.0" +version = "1.12.0" license = "GPL-3.0" authors = ["Parity Technologies "] [dependencies] ethcore = { path = "../ethcore" } ethcore-bytes = { path = "../util/bytes" } -ethereum-types = "0.2" +ethereum-types = "0.3" jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.11" } jsonrpc-http-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.11" } rlp = { path = "../util/rlp" } cid = "0.2" multihash = "0.7" unicase = "2.0" + +[dev-dependencies] +ethcore = { path = "../ethcore", features = ["test-helpers"] } diff --git a/ipfs/src/error.rs b/ipfs/src/error.rs index fadd75b9b4ab0bfc0ce0d978278b3a01178a5380..1ff2829553d59151eefe4536e355a3fb94ee2901 100644 --- a/ipfs/src/error.rs +++ b/ipfs/src/error.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ipfs/src/lib.rs b/ipfs/src/lib.rs index bb7d0c3897c923fb0518199de4cabb7f1796387f..7f6ebe77c4be52692d6f049ba46ba4126c610709 100644 --- a/ipfs/src/lib.rs +++ b/ipfs/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/ipfs/src/route.rs b/ipfs/src/route.rs index 2beb4ccc3716d03c30ee715148074c83cfdba46b..8f57fc4d1022de072371e38ac13917b7c170c35e 100644 --- a/ipfs/src/route.rs +++ b/ipfs/src/route.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/json/Cargo.toml b/json/Cargo.toml index 6e84e6da3ae247f71060659ce3fdfac54a856163..0668caf8b24195df007bc473be1a06d5be11c0c4 100644 --- a/json/Cargo.toml +++ b/json/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" authors = ["Parity Technologies "] [dependencies] -ethereum-types = "0.2" +ethereum-types = "0.3" rustc-hex = "1.0" serde = "1.0" serde_json = "1.0" diff --git a/json/src/blockchain/account.rs b/json/src/blockchain/account.rs index 66b5f9b8446c9a35846f5a6a566aa5eeff59f5d2..38a0b1fa9cdf71bc7ce0b03c16738986ca262195 100644 --- a/json/src/blockchain/account.rs +++ b/json/src/blockchain/account.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/json/src/blockchain/block.rs b/json/src/blockchain/block.rs index 503230f09e61c139f080ff3af938d22f241fa5b1..5a6c995658ffb6a32c4334685f1ab7b5e4060bbe 100644 --- a/json/src/blockchain/block.rs +++ b/json/src/blockchain/block.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/json/src/blockchain/blockchain.rs b/json/src/blockchain/blockchain.rs index 9edd753130a1cb6dc4a34d13a888148adb3091b7..9e4d650b85a1fcdcfcb16fee6a581979b0c0b58d 100644 --- a/json/src/blockchain/blockchain.rs +++ b/json/src/blockchain/blockchain.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/json/src/blockchain/header.rs b/json/src/blockchain/header.rs index 667a36bb1b519d2d400cafbc4a95b84afedf5371..ee79a928eab89cc08e5baca205618f911bc25051 100644 --- a/json/src/blockchain/header.rs +++ b/json/src/blockchain/header.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/json/src/blockchain/mod.rs b/json/src/blockchain/mod.rs index e1faa07880f6a4da27ef9f222c3b52bed0147b06..0d8e7ff78f27c08df2f2f448669698ef29dc584b 100644 --- a/json/src/blockchain/mod.rs +++ b/json/src/blockchain/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/json/src/blockchain/state.rs b/json/src/blockchain/state.rs index a64887572b2394c2ed7dd457d74cd8a558b2465c..e23a31efa036146484fdfe7fbb67238102f23e2e 100644 --- a/json/src/blockchain/state.rs +++ b/json/src/blockchain/state.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/json/src/blockchain/test.rs b/json/src/blockchain/test.rs index 018ae767dde3e693785206353c3dc44806979077..792303dc7e4e28c1f1e0889067db29acd45a09e7 100644 --- a/json/src/blockchain/test.rs +++ b/json/src/blockchain/test.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/json/src/blockchain/transaction.rs b/json/src/blockchain/transaction.rs index 6b3550fd78d5d17701e734b2618b0c181ff2db23..f14dd5e33635256d4de1f417f631ab9b5fcc47d8 100644 --- a/json/src/blockchain/transaction.rs +++ b/json/src/blockchain/transaction.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/json/src/bytes.rs b/json/src/bytes.rs index 79ba4f896b8c58efb4d87911abb5b8bc50917092..3eb1f54152675b92fee49781ff1d33958c8f90d0 100644 --- a/json/src/bytes.rs +++ b/json/src/bytes.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/json/src/hash.rs b/json/src/hash.rs index 54aea04365e4400560dc3045c6ac93fe9feaef59..8dac3f6e7366808ab4096d42fcca63355d7f566e 100644 --- a/json/src/hash.rs +++ b/json/src/hash.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -23,7 +23,6 @@ use serde::de::{Error, Visitor}; use rustc_hex::ToHex; use ethereum_types::{H64 as Hash64, H160 as Hash160, H256 as Hash256, H520 as Hash520, Bloom as Hash2048}; - macro_rules! impl_hash { ($name: ident, $inner: ident) => { /// Lenient hash json deserialization for test json files. diff --git a/json/src/lib.rs b/json/src/lib.rs index 3cb1e49f57d596f5fdee2e076c566c3f8339e55f..5d31cd6c97da077d0a3248c13f5a1f3c19ddd87f 100644 --- a/json/src/lib.rs +++ b/json/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/json/src/maybe.rs b/json/src/maybe.rs index 8b74b22c4830eddef46057663ceb1f127cbb6a52..1f77a98ef1d93c360758023112b80a8635d08765 100644 --- a/json/src/maybe.rs +++ b/json/src/maybe.rs @@ -1,3 +1,18 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . //! Deserializer of empty string values into optionals. diff --git a/json/src/misc/account_meta.rs b/json/src/misc/account_meta.rs index 9c4d67286e8368328b6b01d077ff4e34e896a1f2..cb6ed1c877aae6c219b101a9070e67bb88e10ede 100644 --- a/json/src/misc/account_meta.rs +++ b/json/src/misc/account_meta.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/json/src/misc/dapps_settings.rs b/json/src/misc/dapps_settings.rs index 5081c62b28b5af9110c5a919482c18dea6c37f2a..f59f5f1cf6d75b3ecd8062416de3ce8d1be73b22 100644 --- a/json/src/misc/dapps_settings.rs +++ b/json/src/misc/dapps_settings.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/json/src/misc/mod.rs b/json/src/misc/mod.rs index d587f2f1578f566d4a02f3fc6d1514786846de1d..836094f0c08ded02f3b2991f5a1a5db9857bc5b1 100644 --- a/json/src/misc/mod.rs +++ b/json/src/misc/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/json/src/spec/account.rs b/json/src/spec/account.rs index fb41137aa9c5c57f93341e1aff5ea0f1372de4c1..acc6d96b58973f5d3d7c0dd358e64131a1ff8ebe 100644 --- a/json/src/spec/account.rs +++ b/json/src/spec/account.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/json/src/spec/authority_round.rs b/json/src/spec/authority_round.rs index 4dea4f09899c8d7531bec5b7b04bb27c5c44d2bb..e355c6fe951cdc032528c808e85f77aca30e5a4f 100644 --- a/json/src/spec/authority_round.rs +++ b/json/src/spec/authority_round.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -16,6 +16,7 @@ //! Authority params deserialization. +use ethereum_types::Address; use uint::Uint; use super::ValidatorSet; @@ -43,6 +44,13 @@ pub struct AuthorityRoundParams { /// Reward per block in wei. #[serde(rename="blockReward")] pub block_reward: Option, + /// Block at which the block reward contract should start being used. + #[serde(rename="blockRewardContractTransition")] + pub block_reward_contract_transition: Option, + /// Block reward contract address (setting the block reward contract + /// overrides the static block reward definition). + #[serde(rename="blockRewardContractAddress")] + pub block_reward_contract_address: Option

, /// Block at which maximum uncle count should be considered. #[serde(rename="maximumUncleCountTransition")] pub maximum_uncle_count_transition: Option, diff --git a/json/src/spec/basic_authority.rs b/json/src/spec/basic_authority.rs index 0a257f134b12e9a81f78e0cdd526be6d66c83dc0..1e5c6b845619729926077d02253d391fc49eb312 100644 --- a/json/src/spec/basic_authority.rs +++ b/json/src/spec/basic_authority.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/json/src/spec/builtin.rs b/json/src/spec/builtin.rs index 34e9a2df1c18613cde6690cafa4d6e8b6f1a4a6e..850867d0950ffa9d0de53165ea439d5b87ad9e30 100644 --- a/json/src/spec/builtin.rs +++ b/json/src/spec/builtin.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/json/src/spec/engine.rs b/json/src/spec/engine.rs index e2545a5f90d6306ed39820bbce4f897a53786c24..55b9c1b2af2973cc6d2a88895f69737013d58fa4 100644 --- a/json/src/spec/engine.rs +++ b/json/src/spec/engine.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -142,4 +142,3 @@ mod tests { }; } } - diff --git a/json/src/spec/ethash.rs b/json/src/spec/ethash.rs index afa8b596823841bf3eacaed6857ef31bf8f16f18..19fd09662734141efd6653f3f7857a22bc81c917 100644 --- a/json/src/spec/ethash.rs +++ b/json/src/spec/ethash.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -73,21 +73,6 @@ pub struct EthashParams { #[serde(rename="eip100bTransition")] pub eip100b_transition: Option, - /// See main EthashParams docs. - #[serde(rename="eip150Transition")] - pub eip150_transition: Option, - - /// See main EthashParams docs. - #[serde(rename="eip160Transition")] - pub eip160_transition: Option, - - /// See main EthashParams docs. - #[serde(rename="eip161abcTransition")] - pub eip161abc_transition: Option, - /// See main EthashParams docs. - #[serde(rename="eip161dTransition")] - pub eip161d_transition: Option, - /// See main EthashParams docs. #[serde(rename="ecip1010PauseTransition")] pub ecip1010_pause_transition: Option, @@ -190,11 +175,7 @@ mod tests { "difficultyHardforkTransition": "0x59d9", "difficultyHardforkBoundDivisor": "0x0200", "bombDefuseTransition": "0x41", - "eip100bTransition": "0x42", - "eip150Transition": "0x43", - "eip160Transition": "0x45", - "eip161abcTransition": "0x46", - "eip161dTransition": "0x47" + "eip100bTransition": "0x42" } }"#; @@ -237,10 +218,6 @@ mod tests { difficulty_hardfork_bound_divisor: Some(Uint(U256::from(0x0200))), bomb_defuse_transition: Some(Uint(U256::from(0x41))), eip100b_transition: Some(Uint(U256::from(0x42))), - eip150_transition: Some(Uint(U256::from(0x43))), - eip160_transition: Some(Uint(U256::from(0x45))), - eip161abc_transition: Some(Uint(U256::from(0x46))), - eip161d_transition: Some(Uint(U256::from(0x47))), ecip1010_pause_transition: None, ecip1010_continue_transition: None, ecip1017_era_rounds: None, @@ -285,10 +262,6 @@ mod tests { difficulty_hardfork_bound_divisor: None, bomb_defuse_transition: None, eip100b_transition: None, - eip150_transition: None, - eip160_transition: None, - eip161abc_transition: None, - eip161d_transition: None, ecip1010_pause_transition: None, ecip1010_continue_transition: None, ecip1017_era_rounds: None, diff --git a/json/src/spec/genesis.rs b/json/src/spec/genesis.rs index 984053e77ae0dc7f0d1f5bc994d8cf67d069a418..d8e2ad53579df478abce773b0aa4752c79fa29af 100644 --- a/json/src/spec/genesis.rs +++ b/json/src/spec/genesis.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -16,7 +16,7 @@ //! Spec genesis deserialization. -use uint::Uint; +use uint::{Uint, self}; use hash::{Address, H256}; use bytes::Bytes; use spec::Seal; @@ -37,6 +37,7 @@ pub struct Genesis { pub parent_hash: Option, /// Gas limit. #[serde(rename="gasLimit")] + #[serde(deserialize_with="uint::validate_non_zero")] pub gas_limit: Uint, /// Transactions root. #[serde(rename="transactionsRoot")] diff --git a/json/src/spec/hardcoded_sync.rs b/json/src/spec/hardcoded_sync.rs index 548fd66f0faabd724bbe2f14882aad22116ecc2c..8b00b5413b5104c5c4e2a523ede00cf5511d4a39 100644 --- a/json/src/spec/hardcoded_sync.rs +++ b/json/src/spec/hardcoded_sync.rs @@ -1,4 +1,4 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/json/src/spec/mod.rs b/json/src/spec/mod.rs index 285596f14a72a25f8874474b865bd9647958cdd9..26965c887d08522dc49353fbdd8b24b86df929ad 100644 --- a/json/src/spec/mod.rs +++ b/json/src/spec/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/json/src/spec/null_engine.rs b/json/src/spec/null_engine.rs index cfd3d6ce6377eed4b057256597908b4c809198fc..87827bd5b9f4189b984e231912067396b7f36d55 100644 --- a/json/src/spec/null_engine.rs +++ b/json/src/spec/null_engine.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/json/src/spec/params.rs b/json/src/spec/params.rs index 4a1efe2a7f960e3aa1848ec43d896a5f1429e40f..e03fe7081b9815812b443847bc1e451a72ba7f4d 100644 --- a/json/src/spec/params.rs +++ b/json/src/spec/params.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -51,6 +51,21 @@ pub struct Params { #[serde(rename="forkCanonHash")] pub fork_hash: Option, + /// See main EthashParams docs. + #[serde(rename="eip150Transition")] + pub eip150_transition: Option, + + /// See main EthashParams docs. + #[serde(rename="eip160Transition")] + pub eip160_transition: Option, + + /// See main EthashParams docs. + #[serde(rename="eip161abcTransition")] + pub eip161abc_transition: Option, + /// See main EthashParams docs. + #[serde(rename="eip161dTransition")] + pub eip161d_transition: Option, + /// See `CommonParams` docs. #[serde(rename="eip98Transition")] pub eip98_transition: Option, @@ -85,6 +100,9 @@ pub struct Params { #[serde(rename="eip211Transition")] pub eip211_transition: Option, /// See `CommonParams` docs. + #[serde(rename="eip145Transition")] + pub eip145_transition: Option, + /// See `CommonParams` docs. #[serde(rename="eip214Transition")] pub eip214_transition: Option, /// See `CommonParams` docs. @@ -113,6 +131,9 @@ pub struct Params { /// See main EthashParams docs. #[serde(rename="maxCodeSize")] pub max_code_size: Option, + /// Maximum size of transaction RLP payload. + #[serde(rename="maxTransactionSize")] + pub max_transaction_size: Option, /// See main EthashParams docs. #[serde(rename="maxCodeSizeTransition")] pub max_code_size_transition: Option, diff --git a/json/src/spec/seal.rs b/json/src/spec/seal.rs index 6654a309a307773b6d933c3eed380b2bda03da20..b61d141d641f9d3d113ea394c5769b2ba43fbbbb 100644 --- a/json/src/spec/seal.rs +++ b/json/src/spec/seal.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/json/src/spec/spec.rs b/json/src/spec/spec.rs index 7003cb4cfc9e009525e1ed42bfc68684b0b8b54b..2be695689e48ad53aa8bb3c89fe00b95b745a641 100644 --- a/json/src/spec/spec.rs +++ b/json/src/spec/spec.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/json/src/spec/state.rs b/json/src/spec/state.rs index ad6f2e548dc7e3a73f1b6f0953c8e80db56f9a12..d15ad540ce4ee77515c34ff66cbc775ef39d380f 100644 --- a/json/src/spec/state.rs +++ b/json/src/spec/state.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/json/src/spec/tendermint.rs b/json/src/spec/tendermint.rs index 8f3d4c224871119ae1d3459939b19fd15fdd9ea7..e0a6568aa9c7f56aa56fc3e4e54eec87c5d54648 100644 --- a/json/src/spec/tendermint.rs +++ b/json/src/spec/tendermint.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/json/src/spec/validator_set.rs b/json/src/spec/validator_set.rs index 9c6b4e79a1c4956662789135e932ece44ec9bb85..41fa60961a73ff9349950cf43f3c9ec569a4be29 100644 --- a/json/src/spec/validator_set.rs +++ b/json/src/spec/validator_set.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/json/src/state/log.rs b/json/src/state/log.rs index 823979f6279483b9ec5d9b93db342702ddbb5dd1..1e07d9ed1eaea44ad4222aac55a09ed79b51a33d 100644 --- a/json/src/state/log.rs +++ b/json/src/state/log.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/json/src/state/mod.rs b/json/src/state/mod.rs index 316744983c4b175d5e5c50b01c7ea7a9b8084c1b..6037ca514d04e7e7c27bdec53866496a98375943 100644 --- a/json/src/state/mod.rs +++ b/json/src/state/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/json/src/state/state.rs b/json/src/state/state.rs index 9daecaed8e361a8feb240b2292227aebc64693c7..c6837d1fd650dce360f6cf9131219e7bd5922533 100644 --- a/json/src/state/state.rs +++ b/json/src/state/state.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/json/src/state/test.rs b/json/src/state/test.rs index 3a25c007df8a6d8d360df3844dec4c560ce3d303..528a49b5a6603e7b7136f4e8653b388c56e9e7fa 100644 --- a/json/src/state/test.rs +++ b/json/src/state/test.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/json/src/state/transaction.rs b/json/src/state/transaction.rs index 606c40f21f701f65b371afd27f4382d7034f0d78..89edb08692a6256f5af1ec9befa5023b86698eac 100644 --- a/json/src/state/transaction.rs +++ b/json/src/state/transaction.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/json/src/test/mod.rs b/json/src/test/mod.rs index 1a6e4db7daf7d5d73cf06dc1c498a235a9fd1996..8f95a9aec49378c90b4cc7ac0a581bfab3fd8283 100644 --- a/json/src/test/mod.rs +++ b/json/src/test/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -64,4 +64,3 @@ impl DifficultyTest { serde_json::from_reader(reader) } } - diff --git a/json/src/transaction/mod.rs b/json/src/transaction/mod.rs index 5cde3eff409cdc1885674089ace3f3bc8fc86c30..8ebab3f1c2ba43311b9637929de455961ae65e83 100644 --- a/json/src/transaction/mod.rs +++ b/json/src/transaction/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/json/src/transaction/test.rs b/json/src/transaction/test.rs index a2ef9ad36a9f773e3b0aa38dd77dc81a3f5b91f3..e1bd588de35054a2ffd2a8cb54e99269197f5ddc 100644 --- a/json/src/transaction/test.rs +++ b/json/src/transaction/test.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/json/src/transaction/transaction.rs b/json/src/transaction/transaction.rs index d9b6abb14ec1d451fe7e7a70952d88e893d8e39e..13b342b3f6a300f11fac63872d36c165ef07da6d 100644 --- a/json/src/transaction/transaction.rs +++ b/json/src/transaction/transaction.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/json/src/transaction/txtest.rs b/json/src/transaction/txtest.rs index 33bc0152f262f47f3d0482a764fbe05e55a1685d..60d65e70d66b4b6f63457bd2c5a8e4f105739f07 100644 --- a/json/src/transaction/txtest.rs +++ b/json/src/transaction/txtest.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/json/src/trie/input.rs b/json/src/trie/input.rs index c84f1aa1e1fe432bf2b6edbad643d34db6440460..e1c46ac537e35f6a97dc14a67b90bfc7f4aded3a 100644 --- a/json/src/trie/input.rs +++ b/json/src/trie/input.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/json/src/trie/mod.rs b/json/src/trie/mod.rs index ce1992205846c05834beda8e1ea85b6e559cea74..5dc52cb21d452879b6309b8b7387abbfae6814cb 100644 --- a/json/src/trie/mod.rs +++ b/json/src/trie/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/json/src/trie/test.rs b/json/src/trie/test.rs index 30811ca66190ffd03c463113618964daa9075ef3..c6cd99c25e8c6d1c15e29cd3a26dc73ac0be2edb 100644 --- a/json/src/trie/test.rs +++ b/json/src/trie/test.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/json/src/trie/trie.rs b/json/src/trie/trie.rs index e4951f81413779e0d3f0cd1ea24b11fd7456576b..ca18de7daa5285835304751119d7886f9f1b489c 100644 --- a/json/src/trie/trie.rs +++ b/json/src/trie/trie.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/json/src/uint.rs b/json/src/uint.rs index 70e0390a34cbbf74b258f29bcfe731b15394fc4c..25c5049c45b0da6d04c2d1d4d578aefed0b311ef 100644 --- a/json/src/uint.rs +++ b/json/src/uint.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/json/src/vm/call.rs b/json/src/vm/call.rs index 39d5a828eb9e36fea46dbe1a302d881d136d9274..026951c028212805850f3d817ca91d12ce30ee49 100644 --- a/json/src/vm/call.rs +++ b/json/src/vm/call.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/json/src/vm/env.rs b/json/src/vm/env.rs index c7f0ccd725ffefb4348453c253ccd2832e24492f..f4af8119c30ebf76884feb431079320a3b819136 100644 --- a/json/src/vm/env.rs +++ b/json/src/vm/env.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/json/src/vm/mod.rs b/json/src/vm/mod.rs index a2588e37c79651130d3e524dab5f83d961db96c0..29b12d4805dd8b118a4711635ca6b39a16a542a6 100644 --- a/json/src/vm/mod.rs +++ b/json/src/vm/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/json/src/vm/test.rs b/json/src/vm/test.rs index 68112e60153f70edb14ef1c1358f02825569472c..10b4aae54f826dcad5b0f8596d37d543a8c044d9 100644 --- a/json/src/vm/test.rs +++ b/json/src/vm/test.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/json/src/vm/transaction.rs b/json/src/vm/transaction.rs index efdad0f9cc9c79ffc33172de3f9d0b62b28d8881..44b79e86226a12f11aead6f9d98b1178c759c185 100644 --- a/json/src/vm/transaction.rs +++ b/json/src/vm/transaction.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/json/src/vm/vm.rs b/json/src/vm/vm.rs index c7e33b609a7a4da604148e1512fabe0abea2f414..7fd101da83a633a0d6a6d56cc09fc063eca145db 100644 --- a/json/src/vm/vm.rs +++ b/json/src/vm/vm.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -22,7 +22,7 @@ use hash::H256; use blockchain::State; use vm::{Transaction, Call, Env}; -/// Reporesents vm execution environment before and after exeuction of transaction. +/// Represents vm execution environment before and after execution of transaction. #[derive(Debug, PartialEq, Deserialize)] pub struct Vm { /// Contract calls made internaly by executed transaction. diff --git a/license_header b/license_header index f90ec463dc42a4d721c3bbeac9067e7d2207d5ab..4738554f915dbe740c1b359503840181883cf565 100644 --- a/license_header +++ b/license_header @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/local-store/Cargo.toml b/local-store/Cargo.toml index 6d09eb76f69ade2ad53ce0da4cdaddbe76e75fdc..d2c3469ca40609b33503a7661295b9493a4e7493 100644 --- a/local-store/Cargo.toml +++ b/local-store/Cargo.toml @@ -16,5 +16,6 @@ serde_derive = "1.0" serde_json = "1.0" [dev-dependencies] +ethcore = { path = "../ethcore", features = ["test-helpers"] } ethkey = { path = "../ethkey" } kvdb-memorydb = { path = "../util/kvdb-memorydb" } diff --git a/local-store/src/lib.rs b/local-store/src/lib.rs index 9120b8694a15932e7a1405978da20b241fc23745..83bc07b901549b814730712bf6409303f8e8d837 100644 --- a/local-store/src/lib.rs +++ b/local-store/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -18,6 +18,7 @@ use std::sync::Arc; use std::fmt; +use std::time::Duration; use transaction::{ SignedTransaction, PendingTransaction, UnverifiedTransaction, @@ -25,7 +26,7 @@ use transaction::{ }; use ethcore::client::ClientIoMessage; use io::IoHandler; -use rlp::UntrustedRlp; +use rlp::Rlp; use kvdb::KeyValueDB; extern crate ethcore; @@ -50,7 +51,7 @@ extern crate kvdb_memorydb; const LOCAL_TRANSACTIONS_KEY: &'static [u8] = &*b"LOCAL_TXS"; const UPDATE_TIMER: ::io::TimerToken = 0; -const UPDATE_TIMEOUT_MS: u64 = 15 * 60 * 1000; // once every 15 minutes. +const UPDATE_TIMEOUT: Duration = Duration::from_secs(15 * 60); // once every 15 minutes. /// Errors which can occur while using the local data store. #[derive(Debug)] @@ -102,7 +103,7 @@ struct TransactionEntry { impl TransactionEntry { fn into_pending(self) -> Option { - let tx: UnverifiedTransaction = match UntrustedRlp::new(&self.rlp_bytes).as_val() { + let tx: UnverifiedTransaction = match Rlp::new(&self.rlp_bytes).as_val() { Err(e) => { warn!(target: "local_store", "Invalid persistent transaction stored: {}", e); return None @@ -205,7 +206,7 @@ impl LocalDataStore { impl IoHandler for LocalDataStore { fn initialize(&self, io: &::io::IoContext) { - if let Err(e) = io.register_timer(UPDATE_TIMER, UPDATE_TIMEOUT_MS) { + if let Err(e) = io.register_timer(UPDATE_TIMER, UPDATE_TIMEOUT) { warn!(target: "local_store", "Error registering local store update timer: {}", e); } } diff --git a/logger/Cargo.toml b/logger/Cargo.toml index 0e1b03f9411e8a5e8f2b997e3e5dfdd8c2d084ed..a1f793769c584f34a1af80dec6ce2ed46f13cb43 100644 --- a/logger/Cargo.toml +++ b/logger/Cargo.toml @@ -1,14 +1,14 @@ [package] description = "Log implementation for Parity" name = "ethcore-logger" -version = "1.11.0" +version = "1.12.0" license = "GPL-3.0" authors = ["Parity Technologies "] [dependencies] log = "0.3" env_logger = "0.4" -isatty = "0.1" +atty = "0.2" lazy_static = "1.0" regex = "0.2" time = "0.1" diff --git a/logger/src/lib.rs b/logger/src/lib.rs index a2cf6d2a02d00982b8d1334cf24f4113affbdf05..2a509698028b8851fb4685c39ff64a63bad0bacf 100644 --- a/logger/src/lib.rs +++ b/logger/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -16,23 +16,23 @@ //! Logger for parity executables +extern crate ansi_term; extern crate arrayvec; +extern crate atty; +extern crate env_logger; extern crate log as rlog; -extern crate isatty; +extern crate parking_lot; extern crate regex; -extern crate env_logger; extern crate time; + #[macro_use] extern crate lazy_static; -extern crate parking_lot; -extern crate ansi_term; mod rotating; use std::{env, thread, fs}; use std::sync::{Weak, Arc}; use std::io::Write; -use isatty::{stderr_isatty, stdout_isatty}; use env_logger::LogBuilder; use regex::Regex; use ansi_term::Colour; @@ -86,7 +86,7 @@ pub fn setup_log(config: &Config) -> Result, String> { builder.parse(s); } - let isatty = stderr_isatty(); + let isatty = atty::is(atty::Stream::Stderr); let enable_color = config.color && isatty; let logs = Arc::new(RotatingLogger::new(levels)); let logger = logs.clone(); @@ -122,7 +122,7 @@ pub fn setup_log(config: &Config) -> Result, String> { let _ = file.write_all(b"\n"); } logger.append(removed_color); - if !isatty && record.level() <= LogLevel::Info && stdout_isatty() { + if !isatty && record.level() <= LogLevel::Info && atty::is(atty::Stream::Stdout) { // duplicate INFO/WARN output to console println!("{}", ret); } diff --git a/logger/src/rotating.rs b/logger/src/rotating.rs index e67bdfaad0adcd27e38fb4915c0e299cd3da39bf..ddc24792aecebedbe8d0020ab33627a8aa38fceb 100644 --- a/logger/src/rotating.rs +++ b/logger/src/rotating.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -121,4 +121,3 @@ mod test { assert_eq!(logs.len(), 2); } } - diff --git a/mac/Parity Ethereum.xcodeproj/project.pbxproj b/mac/Parity Ethereum.xcodeproj/project.pbxproj deleted file mode 100644 index 2e41be6377eff384240754d35897949d74be98ac..0000000000000000000000000000000000000000 --- a/mac/Parity Ethereum.xcodeproj/project.pbxproj +++ /dev/null @@ -1,333 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - 0A7A475D1E3D2CDD0093D1AB /* parity in CopyFiles */ = {isa = PBXBuildFile; fileRef = 0A7A475C1E3D2CDD0093D1AB /* parity */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; - 0ACF9AC21E30FAB600D5C935 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ACF9AC11E30FAB600D5C935 /* AppDelegate.swift */; }; - 0ACF9AC41E30FAB600D5C935 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0ACF9AC31E30FAB600D5C935 /* Assets.xcassets */; }; - 0ACF9AC71E30FAB600D5C935 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0ACF9AC51E30FAB600D5C935 /* MainMenu.xib */; }; - 0AE564F11E3CE42C00BD01F7 /* GetBSDProcessList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AE564F01E3CE42C00BD01F7 /* GetBSDProcessList.swift */; }; - 0AED4DA01E3E22F800BF87C0 /* ethstore in CopyFiles */ = {isa = PBXBuildFile; fileRef = 0AED4D9F1E3E22F800BF87C0 /* ethstore */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; - 84CF92B3200E559900AD6E78 /* parity-evm in CopyFiles */ = {isa = PBXBuildFile; fileRef = 84CF92B2200E559900AD6E78 /* parity-evm */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; - 84CF92B6200E56AE00AD6E78 /* ethkey in CopyFiles */ = {isa = PBXBuildFile; fileRef = 84CF92B5200E56AE00AD6E78 /* ethkey */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; -/* End PBXBuildFile section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 0A7A475B1E3D2C800093D1AB /* CopyFiles */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 6; - files = ( - 84CF92B6200E56AE00AD6E78 /* ethkey in CopyFiles */, - 84CF92B3200E559900AD6E78 /* parity-evm in CopyFiles */, - 0AED4DA01E3E22F800BF87C0 /* ethstore in CopyFiles */, - 0A7A475D1E3D2CDD0093D1AB /* parity in CopyFiles */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 0A7A475C1E3D2CDD0093D1AB /* parity */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = parity; path = ../target/release/parity; sourceTree = ""; }; - 0ACF9ABE1E30FAB600D5C935 /* Parity Ethereum.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Parity Ethereum.app"; sourceTree = BUILT_PRODUCTS_DIR; }; - 0ACF9AC11E30FAB600D5C935 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 0ACF9AC31E30FAB600D5C935 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 0ACF9AC61E30FAB600D5C935 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; - 0ACF9AC81E30FAB600D5C935 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 0AE564F01E3CE42C00BD01F7 /* GetBSDProcessList.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GetBSDProcessList.swift; sourceTree = ""; }; - 0AED4D9F1E3E22F800BF87C0 /* ethstore */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = ethstore; path = ../target/release/ethstore; sourceTree = ""; }; - 84CF92B2200E559900AD6E78 /* parity-evm */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = "parity-evm"; path = "../target/release/parity-evm"; sourceTree = ""; }; - 84CF92B5200E56AE00AD6E78 /* ethkey */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = ethkey; path = ../target/release/ethkey; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 0ACF9ABB1E30FAB600D5C935 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 0ACF9AB51E30FAB600D5C935 = { - isa = PBXGroup; - children = ( - 84CF92B5200E56AE00AD6E78 /* ethkey */, - 84CF92B2200E559900AD6E78 /* parity-evm */, - 0AED4D9F1E3E22F800BF87C0 /* ethstore */, - 0A7A475C1E3D2CDD0093D1AB /* parity */, - 0ACF9AC01E30FAB600D5C935 /* Parity Ethereum */, - 0ACF9ABF1E30FAB600D5C935 /* Products */, - ); - sourceTree = ""; - }; - 0ACF9ABF1E30FAB600D5C935 /* Products */ = { - isa = PBXGroup; - children = ( - 0ACF9ABE1E30FAB600D5C935 /* Parity Ethereum.app */, - ); - name = Products; - sourceTree = ""; - }; - 0ACF9AC01E30FAB600D5C935 /* Parity Ethereum */ = { - isa = PBXGroup; - children = ( - 0ACF9AC11E30FAB600D5C935 /* AppDelegate.swift */, - 0ACF9AC31E30FAB600D5C935 /* Assets.xcassets */, - 0ACF9AC51E30FAB600D5C935 /* MainMenu.xib */, - 0ACF9AC81E30FAB600D5C935 /* Info.plist */, - 0AE564F01E3CE42C00BD01F7 /* GetBSDProcessList.swift */, - ); - name = "Parity Ethereum"; - path = Parity; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 0ACF9ABD1E30FAB600D5C935 /* Parity Ethereum */ = { - isa = PBXNativeTarget; - buildConfigurationList = 0ACF9ACB1E30FAB600D5C935 /* Build configuration list for PBXNativeTarget "Parity Ethereum" */; - buildPhases = ( - 0ACF9ABA1E30FAB600D5C935 /* Sources */, - 0ACF9ABB1E30FAB600D5C935 /* Frameworks */, - 0ACF9ABC1E30FAB600D5C935 /* Resources */, - 0A7A475B1E3D2C800093D1AB /* CopyFiles */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "Parity Ethereum"; - productName = Parity; - productReference = 0ACF9ABE1E30FAB600D5C935 /* Parity Ethereum.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 0ACF9AB61E30FAB600D5C935 /* Project object */ = { - isa = PBXProject; - attributes = { - LastSwiftUpdateCheck = 0800; - LastUpgradeCheck = 0820; - ORGANIZATIONNAME = "Parity Technologies"; - TargetAttributes = { - 0ACF9ABD1E30FAB600D5C935 = { - CreatedOnToolsVersion = 8.0; - DevelopmentTeam = P2PX3JU8FT; - ProvisioningStyle = Manual; - }; - }; - }; - buildConfigurationList = 0ACF9AB91E30FAB600D5C935 /* Build configuration list for PBXProject "Parity Ethereum" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 0ACF9AB51E30FAB600D5C935; - productRefGroup = 0ACF9ABF1E30FAB600D5C935 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 0ACF9ABD1E30FAB600D5C935 /* Parity Ethereum */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 0ACF9ABC1E30FAB600D5C935 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 0ACF9AC41E30FAB600D5C935 /* Assets.xcassets in Resources */, - 0ACF9AC71E30FAB600D5C935 /* MainMenu.xib in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 0ACF9ABA1E30FAB600D5C935 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 0AE564F11E3CE42C00BD01F7 /* GetBSDProcessList.swift in Sources */, - 0ACF9AC21E30FAB600D5C935 /* AppDelegate.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXVariantGroup section */ - 0ACF9AC51E30FAB600D5C935 /* MainMenu.xib */ = { - isa = PBXVariantGroup; - children = ( - 0ACF9AC61E30FAB600D5C935 /* Base */, - ); - name = MainMenu.xib; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 0ACF9AC91E30FAB600D5C935 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_SUSPICIOUS_MOVES = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.12; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = macosx; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - }; - name = Debug; - }; - 0ACF9ACA1E30FAB600D5C935 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_SUSPICIOUS_MOVES = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.12; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = macosx; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - }; - name = Release; - }; - 0ACF9ACC1E30FAB600D5C935 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_IDENTITY = "Mac Developer"; - COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = P2PX3JU8FT; - GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; - INFOPLIST_FILE = Parity/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = io.parity.ethereum; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; - }; - name = Debug; - }; - 0ACF9ACD1E30FAB600D5C935 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_IDENTITY = "Developer ID Application"; - COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = P2PX3JU8FT; - GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; - INFOPLIST_FILE = Parity/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = io.parity.ethereum; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 0ACF9AB91E30FAB600D5C935 /* Build configuration list for PBXProject "Parity Ethereum" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 0ACF9AC91E30FAB600D5C935 /* Debug */, - 0ACF9ACA1E30FAB600D5C935 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 0ACF9ACB1E30FAB600D5C935 /* Build configuration list for PBXNativeTarget "Parity Ethereum" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 0ACF9ACC1E30FAB600D5C935 /* Debug */, - 0ACF9ACD1E30FAB600D5C935 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 0ACF9AB61E30FAB600D5C935 /* Project object */; -} diff --git a/mac/Parity Ethereum.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/mac/Parity Ethereum.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 8297038e3bb1baca69c4fbafe833dd5e3693a0bf..0000000000000000000000000000000000000000 --- a/mac/Parity Ethereum.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/mac/Parity.pkgproj b/mac/Parity.pkgproj deleted file mode 100755 index e5229f927a36d3e5aca59924b89a0037fa6e0855..0000000000000000000000000000000000000000 --- a/mac/Parity.pkgproj +++ /dev/null @@ -1,810 +0,0 @@ - - - - - PACKAGES - - - PACKAGE_FILES - - DEFAULT_INSTALL_LOCATION - / - HIERARCHY - - CHILDREN - - - CHILDREN - - - CHILDREN - - GID - 80 - PATH - build/release/Parity Ethereum.app - PATH_TYPE - 3 - PERMISSIONS - 493 - TYPE - 3 - UID - 0 - - - GID - 80 - PATH - Applications - PATH_TYPE - 0 - PERMISSIONS - 509 - TYPE - 1 - UID - 0 - - - CHILDREN - - - CHILDREN - - GID - 80 - PATH - Application Support - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - Automator - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - Documentation - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - Extensions - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - Filesystems - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - Frameworks - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - Input Methods - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - Internet Plug-Ins - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - LaunchAgents - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - LaunchDaemons - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - PreferencePanes - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - Preferences - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 80 - PATH - Printers - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - PrivilegedHelperTools - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - QuickLook - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - QuickTime - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - Screen Savers - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - Scripts - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - Services - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - Widgets - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - GID - 0 - PATH - Library - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - - CHILDREN - - GID - 0 - PATH - Shared - PATH_TYPE - 0 - PERMISSIONS - 1023 - TYPE - 1 - UID - 0 - - - GID - 80 - PATH - Users - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - GID - 0 - PATH - / - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - PAYLOAD_TYPE - 0 - SPLIT_FORKS - - VERSION - 4 - - PACKAGE_SCRIPTS - - POSTINSTALL_PATH - - PATH - post-install.sh - PATH_TYPE - 3 - - RESOURCES - - - PACKAGE_SETTINGS - - AUTHENTICATION - - CONCLUSION_ACTION - 0 - IDENTIFIER - io.parity.ethereum - NAME - Parity - OVERWRITE_PERMISSIONS - - VERSION - 1.11.0 - - UUID - 2DCD5B81-7BAF-4DA1-9251-6274B089FD36 - - - PROJECT - - PROJECT_COMMENTS - - NOTES - - PCFET0NUWVBFIGh0bWwgUFVCTElDICItLy9XM0MvL0RURCBIVE1M - IDQuMDEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvVFIvaHRtbDQv - c3RyaWN0LmR0ZCI+CjxodG1sPgo8aGVhZD4KPG1ldGEgaHR0cC1l - cXVpdj0iQ29udGVudC1UeXBlIiBjb250ZW50PSJ0ZXh0L2h0bWw7 - IGNoYXJzZXQ9VVRGLTgiPgo8bWV0YSBodHRwLWVxdWl2PSJDb250 - ZW50LVN0eWxlLVR5cGUiIGNvbnRlbnQ9InRleHQvY3NzIj4KPHRp - dGxlPjwvdGl0bGU+CjxtZXRhIG5hbWU9IkdlbmVyYXRvciIgY29u - dGVudD0iQ29jb2EgSFRNTCBXcml0ZXIiPgo8bWV0YSBuYW1lPSJD - b2NvYVZlcnNpb24iIGNvbnRlbnQ9IjE0MDQuNDciPgo8c3R5bGUg - dHlwZT0idGV4dC9jc3MiPgo8L3N0eWxlPgo8L2hlYWQ+Cjxib2R5 - Pgo8L2JvZHk+CjwvaHRtbD4K - - - PROJECT_PRESENTATION - - INSTALLATION_STEPS - - - ICPRESENTATION_CHAPTER_VIEW_CONTROLLER_CLASS - ICPresentationViewIntroductionController - INSTALLER_PLUGIN - Introduction - LIST_TITLE_KEY - InstallerSectionTitle - - - ICPRESENTATION_CHAPTER_VIEW_CONTROLLER_CLASS - ICPresentationViewReadMeController - INSTALLER_PLUGIN - ReadMe - LIST_TITLE_KEY - InstallerSectionTitle - - - ICPRESENTATION_CHAPTER_VIEW_CONTROLLER_CLASS - ICPresentationViewLicenseController - INSTALLER_PLUGIN - License - LIST_TITLE_KEY - InstallerSectionTitle - - - ICPRESENTATION_CHAPTER_VIEW_CONTROLLER_CLASS - ICPresentationViewDestinationSelectController - INSTALLER_PLUGIN - TargetSelect - LIST_TITLE_KEY - InstallerSectionTitle - - - ICPRESENTATION_CHAPTER_VIEW_CONTROLLER_CLASS - ICPresentationViewInstallationTypeController - INSTALLER_PLUGIN - PackageSelection - LIST_TITLE_KEY - InstallerSectionTitle - - - ICPRESENTATION_CHAPTER_VIEW_CONTROLLER_CLASS - ICPresentationViewInstallationController - INSTALLER_PLUGIN - Install - LIST_TITLE_KEY - InstallerSectionTitle - - - ICPRESENTATION_CHAPTER_VIEW_CONTROLLER_CLASS - ICPresentationViewSummaryController - INSTALLER_PLUGIN - Summary - LIST_TITLE_KEY - InstallerSectionTitle - - - INTRODUCTION - - LOCALIZATIONS - - - LICENSE - - KEYWORDS - - LOCALIZATIONS - - - LANGUAGE - English - VALUE - - PATH - install-licence.txt - PATH_TYPE - 3 - - - - MODE - 0 - - README - - LOCALIZATIONS - - - LANGUAGE - English - VALUE - - PATH - install-readme.txt - PATH_TYPE - 3 - - - - - TITLE - - LOCALIZATIONS - - - LANGUAGE - English - VALUE - Parity - - - - - PROJECT_REQUIREMENTS - - LIST - - POSTINSTALL_PATH - - PREINSTALL_PATH - - RESOURCES - - ROOT_VOLUME_ONLY - - - PROJECT_SETTINGS - - ADVANCED_OPTIONS - - BUILD_FORMAT - 0 - BUILD_PATH - - PATH - ../target/release - PATH_TYPE - 1 - - EXCLUDED_FILES - - - PATTERNS_ARRAY - - - REGULAR_EXPRESSION - - STRING - .DS_Store - TYPE - 0 - - - PROTECTED - - PROXY_NAME - Remove .DS_Store files - PROXY_TOOLTIP - Remove ".DS_Store" files created by the Finder. - STATE - - - - PATTERNS_ARRAY - - - REGULAR_EXPRESSION - - STRING - .pbdevelopment - TYPE - 0 - - - PROTECTED - - PROXY_NAME - Remove .pbdevelopment files - PROXY_TOOLTIP - Remove ".pbdevelopment" files created by ProjectBuilder or Xcode. - STATE - - - - PATTERNS_ARRAY - - - REGULAR_EXPRESSION - - STRING - CVS - TYPE - 1 - - - REGULAR_EXPRESSION - - STRING - .cvsignore - TYPE - 0 - - - REGULAR_EXPRESSION - - STRING - .cvspass - TYPE - 0 - - - REGULAR_EXPRESSION - - STRING - .svn - TYPE - 1 - - - REGULAR_EXPRESSION - - STRING - .git - TYPE - 1 - - - REGULAR_EXPRESSION - - STRING - .gitignore - TYPE - 0 - - - PROTECTED - - PROXY_NAME - Remove SCM metadata - PROXY_TOOLTIP - Remove helper files and folders used by the CVS, SVN or Git Source Code Management systems. - STATE - - - - PATTERNS_ARRAY - - - REGULAR_EXPRESSION - - STRING - classes.nib - TYPE - 0 - - - REGULAR_EXPRESSION - - STRING - designable.db - TYPE - 0 - - - REGULAR_EXPRESSION - - STRING - info.nib - TYPE - 0 - - - PROTECTED - - PROXY_NAME - Optimize nib files - PROXY_TOOLTIP - Remove "classes.nib", "info.nib" and "designable.nib" files within .nib bundles. - STATE - - - - PATTERNS_ARRAY - - - REGULAR_EXPRESSION - - STRING - Resources Disabled - TYPE - 1 - - - PROTECTED - - PROXY_NAME - Remove Resources Disabled folders - PROXY_TOOLTIP - Remove "Resources Disabled" folders. - STATE - - - - SEPARATOR - - - - NAME - Parity Ethereum - - - TYPE - 0 - VERSION - 2 - - diff --git a/mac/Parity/AppDelegate.swift b/mac/Parity/AppDelegate.swift deleted file mode 100644 index d65c1d601fab4187cb6b4216b3c1ba0efe63f117..0000000000000000000000000000000000000000 --- a/mac/Parity/AppDelegate.swift +++ /dev/null @@ -1,223 +0,0 @@ -// Copyright 2015-2018 Parity Technologies (UK) Ltd. -// This file is part of Parity. - -// Parity is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity. If not, see . - -import Cocoa - -@NSApplicationMain -@available(macOS, deprecated: 10.11) - -class AppDelegate: NSObject, NSApplicationDelegate { - @IBOutlet weak var statusMenu: NSMenu! - @IBOutlet weak var startAtLogonMenuItem: NSMenuItem! - - let statusItem = NSStatusBar.system().statusItem(withLength: NSVariableStatusItemLength) - var parityPid: Int32? = nil - var commandLine: [String] = [] - let defaultDefaults = "{\"fat_db\":false,\"mode\":\"passive\",\"mode.alarm\":3600,\"mode.timeout\":300,\"pruning\":\"fast\",\"tracing\":false}" - - func menuAppPath() -> String { - return Bundle.main.executablePath! - } - - func parityPath() -> String { - return Bundle.main.bundlePath + "/Contents/MacOS/parity" - } - - func isAlreadyRunning() -> Bool { - return NSRunningApplication.runningApplications(withBundleIdentifier: Bundle.main.bundleIdentifier!).count > 1 - - } - - func isParityRunning() -> Bool { - if let pid = self.parityPid { - return kill(pid, 0) == 0 - } - return false - } - - func killParity() { - if let pid = self.parityPid { - kill(pid, SIGKILL) - } - } - - func openUI() { - let parity = Process() - parity.launchPath = self.parityPath() - parity.arguments = self.commandLine - parity.arguments!.append("ui") - parity.launch() - } - - func writeConfigFiles() { - let basePath = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first? - .appendingPathComponent(Bundle.main.bundleIdentifier!, isDirectory: true) - - if FileManager.default.fileExists(atPath: basePath!.path) { - return - } - - do { - let defaultsFileDir = basePath?.appendingPathComponent("chains").appendingPathComponent("ethereum") - let defaultsFile = defaultsFileDir?.appendingPathComponent("user_defaults") - - try FileManager.default.createDirectory(atPath: (defaultsFileDir?.path)!, withIntermediateDirectories: true, attributes: nil) - if !FileManager.default.fileExists(atPath: defaultsFile!.path) { - try defaultDefaults.write(to: defaultsFile!, atomically: false, encoding: String.Encoding.utf8) - } - - let configFile = basePath?.appendingPathComponent("config.toml") - } - catch {} - } - - func autostartEnabled() -> Bool { - return itemReferencesInLoginItems().existingReference != nil - } - - func itemReferencesInLoginItems() -> (existingReference: LSSharedFileListItem?, lastReference: LSSharedFileListItem?) { - let itemUrl: UnsafeMutablePointer?> = UnsafeMutablePointer?>.allocate(capacity: 1) - if let appUrl: NSURL = NSURL.fileURL(withPath: Bundle.main.bundlePath) as NSURL? { - let loginItemsRef = LSSharedFileListCreate( - nil, - kLSSharedFileListSessionLoginItems.takeRetainedValue(), - nil - ).takeRetainedValue() as LSSharedFileList? - if loginItemsRef != nil { - let loginItems: NSArray = LSSharedFileListCopySnapshot(loginItemsRef, nil).takeRetainedValue() as NSArray - if(loginItems.count > 0) - { - let lastItemRef: LSSharedFileListItem = loginItems.lastObject as! LSSharedFileListItem - for i in 0 ..< loginItems.count { - let currentItemRef: LSSharedFileListItem = loginItems.object(at: i) as! LSSharedFileListItem - if LSSharedFileListItemResolve(currentItemRef, 0, itemUrl, nil) == noErr { - if let urlRef: NSURL = itemUrl.pointee?.takeRetainedValue() { - if urlRef.isEqual(appUrl) { - return (currentItemRef, lastItemRef) - } - } - } - } - //The application was not found in the startup list - return (nil, lastItemRef) - } - else - { - let addAtStart: LSSharedFileListItem = kLSSharedFileListItemBeforeFirst.takeRetainedValue() - return(nil, addAtStart) - } - } - } - return (nil, nil) - } - - func toggleLaunchAtStartup() { - let itemReferences = itemReferencesInLoginItems() - let shouldBeToggled = (itemReferences.existingReference == nil) - let loginItemsRef = LSSharedFileListCreate( - nil, - kLSSharedFileListSessionLoginItems.takeRetainedValue(), - nil - ).takeRetainedValue() as LSSharedFileList? - if loginItemsRef != nil { - if shouldBeToggled { - if let appUrl : CFURL = NSURL.fileURL(withPath: Bundle.main.bundlePath) as CFURL? { - LSSharedFileListInsertItemURL( - loginItemsRef, - itemReferences.lastReference, - nil, - nil, - appUrl, - nil, - nil - ) - } - } else { - if let itemRef = itemReferences.existingReference { - LSSharedFileListItemRemove(loginItemsRef,itemRef) - } - } - } - } - - func launchParity() { - self.commandLine = CommandLine.arguments.dropFirst().filter({ $0 != "ui"}) - - let processes = GetBSDProcessList()! - let parityProcess = processes.index(where: { - var name = $0.kp_proc.p_comm - let str = withUnsafePointer(to: &name) { - $0.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout.size(ofValue: name)) { - String(cString: $0) - } - } - return str == "parity" - }) - - if parityProcess == nil { - let parity = Process() - let p = self.parityPath() - parity.launchPath = p//self.parityPath() - parity.arguments = self.commandLine - parity.launch() - self.parityPid = parity.processIdentifier - } else { - self.parityPid = processes[parityProcess!].kp_proc.p_pid - } - } - - func applicationDidFinishLaunching(_ aNotification: Notification) { - if self.isAlreadyRunning() { - openUI() - NSApplication.shared().terminate(self) - return - } - - self.writeConfigFiles() - self.launchParity() - Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true, block: {_ in - if !self.isParityRunning() { - NSApplication.shared().terminate(self) - } - }) - - let icon = NSImage(named: "statusIcon") - icon?.isTemplate = true // best for dark mode - statusItem.image = icon - statusItem.menu = statusMenu - } - - override func validateMenuItem(_ menuItem: NSMenuItem) -> Bool { - if menuItem == self.startAtLogonMenuItem! { - menuItem.state = self.autostartEnabled() ? NSOnState : NSOffState - } - return true - } - - @IBAction func quitClicked(_ sender: NSMenuItem) { - self.killParity() - NSApplication.shared().terminate(self) - } - - @IBAction func openClicked(_ sender: NSMenuItem) { - self.openUI() - } - - @IBAction func startAtLogonClicked(_ sender: NSMenuItem) { - self.toggleLaunchAtStartup() - } - -} diff --git a/mac/Parity/Assets.xcassets/AppIcon.appiconset/Contents.json b/mac/Parity/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index 9d59e8b3a56f792f47af2d5deea7d467ff6c2e9f..0000000000000000000000000000000000000000 --- a/mac/Parity/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "images" : [ - { - "idiom" : "mac", - "size" : "16x16", - "scale" : "1x" - }, - { - "idiom" : "mac", - "size" : "16x16", - "scale" : "2x" - }, - { - "idiom" : "mac", - "size" : "32x32", - "scale" : "1x" - }, - { - "idiom" : "mac", - "size" : "32x32", - "scale" : "2x" - }, - { - "idiom" : "mac", - "size" : "128x128", - "scale" : "1x" - }, - { - "idiom" : "mac", - "size" : "128x128", - "scale" : "2x" - }, - { - "idiom" : "mac", - "size" : "256x256", - "scale" : "1x" - }, - { - "idiom" : "mac", - "size" : "256x256", - "scale" : "2x" - }, - { - "size" : "512x512", - "idiom" : "mac", - "filename" : "Parity.png", - "scale" : "1x" - }, - { - "idiom" : "mac", - "size" : "512x512", - "scale" : "2x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/mac/Parity/Assets.xcassets/AppIcon.appiconset/Parity.png b/mac/Parity/Assets.xcassets/AppIcon.appiconset/Parity.png deleted file mode 100644 index a7f085dab7a3db0028b50aa965f0ddb690affcff..0000000000000000000000000000000000000000 Binary files a/mac/Parity/Assets.xcassets/AppIcon.appiconset/Parity.png and /dev/null differ diff --git a/mac/Parity/Assets.xcassets/Contents.json b/mac/Parity/Assets.xcassets/Contents.json deleted file mode 100644 index da4a164c918651cdd1e11dca5cc62c333f097601..0000000000000000000000000000000000000000 --- a/mac/Parity/Assets.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/mac/Parity/Assets.xcassets/statusIcon.imageset/Contents.json b/mac/Parity/Assets.xcassets/statusIcon.imageset/Contents.json deleted file mode 100644 index b709bf58b25c78056da7646498c0b0bad4c6990b..0000000000000000000000000000000000000000 --- a/mac/Parity/Assets.xcassets/statusIcon.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "Parity-1.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "Parity.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "Parity-2.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/mac/Parity/Assets.xcassets/statusIcon.imageset/Parity-1.png b/mac/Parity/Assets.xcassets/statusIcon.imageset/Parity-1.png deleted file mode 100644 index 75767ebd7d6fe6b52b9edc86cd76c7528a3b3ee8..0000000000000000000000000000000000000000 Binary files a/mac/Parity/Assets.xcassets/statusIcon.imageset/Parity-1.png and /dev/null differ diff --git a/mac/Parity/Assets.xcassets/statusIcon.imageset/Parity-2.png b/mac/Parity/Assets.xcassets/statusIcon.imageset/Parity-2.png deleted file mode 100644 index 6a1106f5f1a9e3d7c94c74cd194513ff1a5813d9..0000000000000000000000000000000000000000 Binary files a/mac/Parity/Assets.xcassets/statusIcon.imageset/Parity-2.png and /dev/null differ diff --git a/mac/Parity/Assets.xcassets/statusIcon.imageset/Parity.png b/mac/Parity/Assets.xcassets/statusIcon.imageset/Parity.png deleted file mode 100644 index dfbda8ea3cf35f955d256a20fb65e5a80c9c89fb..0000000000000000000000000000000000000000 Binary files a/mac/Parity/Assets.xcassets/statusIcon.imageset/Parity.png and /dev/null differ diff --git a/mac/Parity/Base.lproj/MainMenu.xib b/mac/Parity/Base.lproj/MainMenu.xib deleted file mode 100644 index 2f52c80ad26832f8ba32b94c5d40db6b64d1fbaa..0000000000000000000000000000000000000000 --- a/mac/Parity/Base.lproj/MainMenu.xib +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/mac/Parity/GetBSDProcessList.swift b/mac/Parity/GetBSDProcessList.swift deleted file mode 100644 index 6737787be8c37c22e3989e4773574f198b1bfe00..0000000000000000000000000000000000000000 --- a/mac/Parity/GetBSDProcessList.swift +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2015-2018 Parity Technologies (UK) Ltd. -// This file is part of Parity. - -// Parity is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity. If not, see . - - -// Based on https://github.com/soh335/GetBSDProcessList - -import Foundation -import Darwin - -public func GetBSDProcessList() -> ([kinfo_proc]?) { - - var done = false - var result: [kinfo_proc]? - var err: Int32 - - repeat { - let name = [CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0]; - let namePointer = name.withUnsafeBufferPointer { UnsafeMutablePointer(mutating: $0.baseAddress) } - var length: Int = 0 - - err = sysctl(namePointer, u_int(name.count), nil, &length, nil, 0) - if err == -1 { - err = errno - } - - if err == 0 { - let count = length / MemoryLayout.stride - result = [kinfo_proc](repeating: kinfo_proc(), count: count) - err = result!.withUnsafeMutableBufferPointer({ ( p: inout UnsafeMutableBufferPointer) -> Int32 in - return sysctl(namePointer, u_int(name.count), p.baseAddress, &length, nil, 0) - }) - switch err { - case 0: - done = true - case -1: - err = errno - case ENOMEM: - err = 0 - default: - fatalError() - } - } - } while err == 0 && !done - - return result -} diff --git a/mac/Parity/Info.plist b/mac/Parity/Info.plist deleted file mode 100644 index 2cc7e67b83e0b91ca65688de43f162e0cef3698b..0000000000000000000000000000000000000000 --- a/mac/Parity/Info.plist +++ /dev/null @@ -1,36 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIconFile - - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.11 - CFBundleVersion - 1 - LSApplicationCategoryType - public.app-category.finance - LSMinimumSystemVersion - $(MACOSX_DEPLOYMENT_TARGET) - LSUIElement - - NSHumanReadableCopyright - Copyright © 2017 Parity Technologies. All rights reserved. - NSMainNibFile - MainMenu - NSPrincipalClass - NSApplication - - diff --git a/mac/install-licence.txt b/mac/install-licence.txt deleted file mode 100644 index 733c072369ca77331f392c40da7404c85c36542c..0000000000000000000000000000000000000000 --- a/mac/install-licence.txt +++ /dev/null @@ -1,675 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - {one line to give the program's name and a brief idea of what it does.} - Copyright (C) {year} {name of author} - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - {project} Copyright (C) {year} {fullname} - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. - diff --git a/mac/install-readme.txt b/mac/install-readme.txt deleted file mode 100644 index 51f0330bbd65fbef3c9d8bb30e0a5fb4e0733fba..0000000000000000000000000000000000000000 --- a/mac/install-readme.txt +++ /dev/null @@ -1,8 +0,0 @@ -Parity Wallet -============= - -Welcome to Parity Wallet, your all-in-one Ethereum node and wallet. - -If you continue, Parity will be installed as a user service. You will be able to use the Parity Wallet through your browser by using the menu bar icon, following the shortcut in the Launchpad or navigating to http://localhost:8180/ in your browser. - -Parity is distributed under the terms of the GPL. diff --git a/mac/post-install.sh b/mac/post-install.sh deleted file mode 100755 index a364878d88938cb95d587cdf9e3c6baf480e8a73..0000000000000000000000000000000000000000 --- a/mac/post-install.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash -# uninstall any ancient version -test -f /usr/local/libexec/uninstall-parity.sh && /usr/local/libexec/uninstall-parity.sh || true -killall -9 parity && sleep 5 -su $USER -c "open /Applications/Parity\ Ethereum.app" -sleep 5 -su $USER -c "open http://127.0.0.1:8180/" -exit 0 diff --git a/mac/uninstall-parity.sh b/mac/uninstall-parity.sh deleted file mode 100755 index 840dba1f6091e5d2b6fa3b7664c3636c11a92334..0000000000000000000000000000000000000000 --- a/mac/uninstall-parity.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - -if [[ "$SUDO_USER" == "" ]] ; then - echo "This script requires elevated privileges." - sudo $0 - exit; -fi - -PLIST=~/Library/LaunchAgents/io.parity.ethereum.plist -su $SUDO_USER -c "launchctl stop io.parity.ethereum" -su $SUDO_USER -c "launchctl unload $PLIST" -rm -f /usr/local/libexec/parity /usr/local/libexec/uninstall-parity.sh /usr/local/bin/ethstore /usr/local/bin/ethkey /usr/local/bin/parity-evm $PLIST diff --git a/machine/Cargo.toml b/machine/Cargo.toml index 037bc55bf806ecbd0356c4fc48964fe3a8d1792a..ee56b9de87c56d279f66371fc1e23648474952b9 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -5,4 +5,4 @@ description = "Generalization of a state machine for consensus engines" authors = ["Parity Technologies "] [dependencies] -ethereum-types = "0.2" +ethereum-types = "0.3" diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 3a45c38d2efb9244847447a42c6432aa84774884..6d152851da675a1638621af340df638a9c344e13 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -40,13 +40,35 @@ pub trait Header { fn number(&self) -> u64; } -/// a header with an associated score (difficulty in PoW terms) +/// A header with an associated score (difficulty in PoW terms) pub trait ScoredHeader: Header { + type Value; + /// Get the score of this header. - fn score(&self) -> &U256; + fn score(&self) -> &Self::Value; /// Set the score of this header. - fn set_score(&mut self, score: U256); + fn set_score(&mut self, score: Self::Value); +} + +/// A header with associated total score. +pub trait TotalScoredHeader: Header { + type Value; + + /// Get the total score of this header. + fn total_score(&self) -> Self::Value; +} + +/// A header with finalized information. +pub trait FinalizableHeader: Header { + /// Get whether this header is considered finalized, so that it will never be replaced in reorganization. + fn is_finalized(&self) -> bool; +} + +/// A header with metadata information. +pub trait WithMetadataHeader: Header { + /// Get the current header metadata. + fn metadata(&self) -> Option<&[u8]>; } /// A "live" block is one which is in the process of the transition. @@ -73,16 +95,36 @@ pub trait Transactions: LiveBlock { fn transactions(&self) -> &[Self::Transaction]; } +/// Trait for blocks which have finalized information. +pub trait Finalizable: LiveBlock { + /// Get whether the block is finalized. + fn is_finalized(&self) -> bool; + /// Mark the block as finalized. + fn mark_finalized(&mut self); +} + +/// A state machine with block metadata. +pub trait WithMetadata: LiveBlock { + /// Get the current live block metadata. + fn metadata(&self) -> Option<&[u8]>; + /// Set the current live block metadata. + fn set_metadata(&mut self, value: Option>); +} + /// Generalization of types surrounding blockchain-suitable state machines. pub trait Machine: for<'a> LocalizedMachine<'a> { /// The block header type. type Header: Header; /// The live block type. type LiveBlock: LiveBlock; + /// Block header with metadata information. + type ExtendedHeader: Header; /// A handle to a blockchain client for this machine. type EngineClient: ?Sized; /// A description of needed auxiliary data. type AuxiliaryRequest; + /// Actions taken on ancestry blocks when commiting a new block. + type AncestryAction; /// Errors which can occur when querying or interacting with the machine. type Error; @@ -106,12 +148,4 @@ pub trait WithBalances: Machine { /// Increment the balance of an account in the state of the live block. fn add_balance(&self, live: &mut Self::LiveBlock, address: &Address, amount: &U256) -> Result<(), Self::Error>; - - /// Note block rewards. "direct" rewards are for authors, "indirect" are for e.g. uncles. - fn note_rewards( - &self, - _live: &mut Self::LiveBlock, - _direct: &[(Address, U256)], - _indirect: &[(Address, U256)], - ) -> Result<(), Self::Error> { Ok(()) } } diff --git a/miner/Cargo.toml b/miner/Cargo.toml index b9f44cd6e02c79bcf55cb553d478521e54af6dcb..e692d2f70838f0a496a2f6c2490e775e2545e70e 100644 --- a/miner/Cargo.toml +++ b/miner/Cargo.toml @@ -3,27 +3,35 @@ description = "Parity Miner interface." name = "ethcore-miner" homepage = "http://parity.io" license = "GPL-3.0" -version = "1.11.0" +version = "1.12.0" authors = ["Parity Technologies "] [dependencies] -# TODO [ToDr] Rewrite using reqwest -hyper = { git = "https://github.com/paritytech/hyper", default-features = false } - -common-types = { path = "../ethcore/types" } -ethabi = "5.1" -ethabi-contract = "5.0" -ethabi-derive = "5.0" +# Only work_notify, consider a separate crate ethash = { path = "../ethash" } +fetch = { path = "../util/fetch" } +hyper = "0.11" +parity-reactor = { path = "../util/reactor" } +url = "1" + +# Miner +ansi_term = "0.10" +error-chain = "0.11" ethcore-transaction = { path = "../ethcore/transaction" } -ethereum-types = "0.2" -ethkey = { path = "../ethkey" } +ethereum-types = "0.3" futures = "0.1" +futures-cpupool = "0.1" heapsize = "0.4" keccak-hash = { path = "../util/hash" } linked-hash-map = "0.5" log = "0.3" parking_lot = "0.5" +price-info = { path = "../price-info" } +rlp = { path = "../util/rlp" } +trace-time = { path = "../util/trace-time" } +transaction-pool = { path = "../transaction-pool" } + +[dev-dependencies] +env_logger = "0.4" +ethkey = { path = "../ethkey" } rustc-hex = "1.0" -table = { path = "../util/table" } -transient-hashmap = "0.4" diff --git a/miner/src/banning_queue.rs b/miner/src/banning_queue.rs deleted file mode 100644 index 388ae5e001543e24864b5c3f15490639bac30d65..0000000000000000000000000000000000000000 --- a/miner/src/banning_queue.rs +++ /dev/null @@ -1,321 +0,0 @@ -// Copyright 2015-2017 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 . - -//! Banning Queue -//! Transacton Queue wrapper maintaining additional list of banned senders and contract hashes. - -use std::time::Duration; -use std::ops::{Deref, DerefMut}; -use ethereum_types::{H256, U256, Address}; -use hash::keccak; -use transaction::{self, SignedTransaction, Action}; -use transient_hashmap::TransientHashMap; - -use transaction_queue::{TransactionQueue, TransactionDetailsProvider, TransactionOrigin, QueuingInstant}; - -type Count = u16; - -/// Auto-Banning threshold -pub enum Threshold { - /// Should ban after given number of misbehaves reported. - BanAfter(Count), - /// Should never ban anything - NeverBan -} - -impl Default for Threshold { - fn default() -> Self { - Threshold::NeverBan - } -} - -/// Transaction queue with banlist. -pub struct BanningTransactionQueue { - queue: TransactionQueue, - ban_threshold: Threshold, - senders_bans: TransientHashMap, - recipients_bans: TransientHashMap, - codes_bans: TransientHashMap, -} - -impl BanningTransactionQueue { - /// Creates new banlisting transaction queue - pub fn new(queue: TransactionQueue, ban_threshold: Threshold, ban_lifetime: Duration) -> Self { - let ban_lifetime_sec = ban_lifetime.as_secs() as u32; - assert!(ban_lifetime_sec > 0, "Lifetime has to be specified in seconds."); - BanningTransactionQueue { - queue: queue, - ban_threshold: ban_threshold, - senders_bans: TransientHashMap::new(ban_lifetime_sec), - recipients_bans: TransientHashMap::new(ban_lifetime_sec), - codes_bans: TransientHashMap::new(ban_lifetime_sec), - } - } - - /// Borrows internal queue. - /// NOTE: you can insert transactions to the queue even - /// if they would be rejected because of ban otherwise. - /// But probably you shouldn't. - pub fn queue(&mut self) -> &mut TransactionQueue { - &mut self.queue - } - - /// Add to the queue taking bans into consideration. - /// May reject transaction because of the banlist. - pub fn add_with_banlist( - &mut self, - transaction: SignedTransaction, - time: QueuingInstant, - details_provider: &TransactionDetailsProvider, - ) -> Result { - if let Threshold::BanAfter(threshold) = self.ban_threshold { - // NOTE In all checks use direct query to avoid increasing ban timeout. - - // Check sender - let sender = transaction.sender(); - let count = self.senders_bans.direct().get(&sender).cloned().unwrap_or(0); - if count > threshold { - debug!(target: "txqueue", "Ignoring transaction {:?} because sender is banned.", transaction.hash()); - return Err(transaction::Error::SenderBanned); - } - - // Check recipient - if let Action::Call(recipient) = transaction.action { - let count = self.recipients_bans.direct().get(&recipient).cloned().unwrap_or(0); - if count > threshold { - debug!(target: "txqueue", "Ignoring transaction {:?} because recipient is banned.", transaction.hash()); - return Err(transaction::Error::RecipientBanned); - } - } - - // Check code - if let Action::Create = transaction.action { - let code_hash = keccak(&transaction.data); - let count = self.codes_bans.direct().get(&code_hash).cloned().unwrap_or(0); - if count > threshold { - debug!(target: "txqueue", "Ignoring transaction {:?} because code is banned.", transaction.hash()); - return Err(transaction::Error::CodeBanned); - } - } - } - self.queue.add(transaction, TransactionOrigin::External, time, None, details_provider) - } - - /// Ban transaction with given hash. - /// Transaction has to be in the queue. - /// - /// Bans sender and recipient/code and returns `true` when any ban has reached threshold. - pub fn ban_transaction(&mut self, hash: &H256) -> bool { - let transaction = self.queue.find(hash); - match transaction { - Some(transaction) => { - let sender = transaction.sender(); - // Ban sender - let sender_banned = self.ban_sender(sender); - // Ban recipient and codehash - let recipient_or_code_banned = match transaction.action { - Action::Call(recipient) => { - self.ban_recipient(recipient) - }, - Action::Create => { - self.ban_codehash(keccak(&transaction.data)) - }, - }; - sender_banned || recipient_or_code_banned - }, - None => false, - } - } - - /// Ban given sender. - /// If bans threshold is reached all subsequent transactions from this sender will be rejected. - /// Reaching bans threshold also removes all existsing transaction from this sender that are already in the - /// queue. - fn ban_sender(&mut self, address: Address) -> bool { - let count = { - let count = self.senders_bans.entry(address).or_insert_with(|| 0); - *count = count.saturating_add(1); - *count - }; - match self.ban_threshold { - Threshold::BanAfter(threshold) if count > threshold => { - // Banlist the sender. - // Remove all transactions from the queue. - self.cull(address, !U256::zero()); - true - }, - _ => false - } - } - - /// Ban given recipient. - /// If bans threshold is reached all subsequent transactions to this address will be rejected. - /// Returns true if bans threshold has been reached. - fn ban_recipient(&mut self, address: Address) -> bool { - let count = { - let count = self.recipients_bans.entry(address).or_insert_with(|| 0); - *count = count.saturating_add(1); - *count - }; - match self.ban_threshold { - // TODO [ToDr] Consider removing other transactions to the same recipient from the queue? - Threshold::BanAfter(threshold) if count > threshold => true, - _ => false - } - } - - - /// Ban given codehash. - /// If bans threshold is reached all subsequent transactions to contracts with this codehash will be rejected. - /// Returns true if bans threshold has been reached. - fn ban_codehash(&mut self, code_hash: H256) -> bool { - let count = self.codes_bans.entry(code_hash).or_insert_with(|| 0); - *count = count.saturating_add(1); - - match self.ban_threshold { - // TODO [ToDr] Consider removing other transactions with the same code from the queue? - Threshold::BanAfter(threshold) if *count > threshold => true, - _ => false, - } - } -} - -impl Deref for BanningTransactionQueue { - type Target = TransactionQueue; - - fn deref(&self) -> &Self::Target { - &self.queue - } -} -impl DerefMut for BanningTransactionQueue { - fn deref_mut(&mut self) -> &mut Self::Target { - self.queue() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use ethkey::{Random, Generator}; - use rustc_hex::FromHex; - use transaction_queue::test::DummyTransactionDetailsProvider; - use ethereum_types::{U256, Address}; - - fn queue() -> BanningTransactionQueue { - BanningTransactionQueue::new(TransactionQueue::default(), Threshold::BanAfter(1), Duration::from_secs(180)) - } - - fn default_tx_provider() -> DummyTransactionDetailsProvider { - DummyTransactionDetailsProvider::default().with_account_nonce(U256::zero()) - } - - fn transaction(action: Action) -> SignedTransaction { - let keypair = Random.generate().unwrap(); - transaction::Transaction { - action: action, - value: U256::from(100), - data: "3331600055".from_hex().unwrap(), - gas: U256::from(100_000), - gas_price: U256::from(10), - nonce: U256::from(0), - }.sign(keypair.secret(), None) - } - - fn unwrap_err(res: Result) -> transaction::Error { - res.unwrap_err() - } - - #[test] - fn should_allow_to_borrow_the_queue() { - // given - let tx = transaction(Action::Create); - let mut txq = queue(); - - // when - txq.queue().add(tx, TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - - // then - // should also deref to queue - assert_eq!(txq.status().pending, 1); - } - - #[test] - fn should_not_accept_transactions_from_banned_sender() { - // given - let tx = transaction(Action::Create); - let mut txq = queue(); - // Banlist once (threshold not reached) - let banlist1 = txq.ban_sender(tx.sender()); - assert!(!banlist1, "Threshold not reached yet."); - // Insert once - let import1 = txq.add_with_banlist(tx.clone(), 0, &default_tx_provider()).unwrap(); - assert_eq!(import1, transaction::ImportResult::Current); - - // when - let banlist2 = txq.ban_sender(tx.sender()); - let import2 = txq.add_with_banlist(tx.clone(), 0, &default_tx_provider()); - - // then - assert!(banlist2, "Threshold should be reached - banned."); - assert_eq!(unwrap_err(import2), transaction::Error::SenderBanned); - // Should also remove transacion from the queue - assert_eq!(txq.find(&tx.hash()), None); - } - - #[test] - fn should_not_accept_transactions_to_banned_recipient() { - // given - let recipient = Address::default(); - let tx = transaction(Action::Call(recipient)); - let mut txq = queue(); - // Banlist once (threshold not reached) - let banlist1 = txq.ban_recipient(recipient); - assert!(!banlist1, "Threshold not reached yet."); - // Insert once - let import1 = txq.add_with_banlist(tx.clone(), 0, &default_tx_provider()).unwrap(); - assert_eq!(import1, transaction::ImportResult::Current); - - // when - let banlist2 = txq.ban_recipient(recipient); - let import2 = txq.add_with_banlist(tx.clone(), 0, &default_tx_provider()); - - // then - assert!(banlist2, "Threshold should be reached - banned."); - assert_eq!(unwrap_err(import2), transaction::Error::RecipientBanned); - } - - #[test] - fn should_not_accept_transactions_with_banned_code() { - // given - let tx = transaction(Action::Create); - let codehash = keccak(&tx.data); - let mut txq = queue(); - // Banlist once (threshold not reached) - let banlist1 = txq.ban_codehash(codehash); - assert!(!banlist1, "Threshold not reached yet."); - // Insert once - let import1 = txq.add_with_banlist(tx.clone(), 0, &default_tx_provider()).unwrap(); - assert_eq!(import1, transaction::ImportResult::Current); - - // when - let banlist2 = txq.ban_codehash(codehash); - let import2 = txq.add_with_banlist(tx.clone(), 0, &default_tx_provider()); - - // then - assert!(banlist2, "Threshold should be reached - banned."); - assert_eq!(unwrap_err(import2), transaction::Error::CodeBanned); - } -} diff --git a/miner/src/external.rs b/miner/src/external.rs index cbd830cd55f98131dead2ae657c7cd5f89b14287..a56be42f02125b0c24a4c5fa3399bc70f58531e7 100644 --- a/miner/src/external.rs +++ b/miner/src/external.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -53,11 +53,11 @@ impl ExternalMiner { } } -const ENTRY_TIMEOUT: u64 = 2; +const ENTRY_TIMEOUT: Duration = Duration::from_secs(2); impl ExternalMinerService for ExternalMiner { fn submit_hashrate(&self, hashrate: U256, id: H256) { - self.hashrates.lock().insert(id, (Instant::now() + Duration::from_secs(ENTRY_TIMEOUT), hashrate)); + self.hashrates.lock().insert(id, (Instant::now() + ENTRY_TIMEOUT, hashrate)); } fn hashrate(&self) -> U256 { @@ -106,7 +106,6 @@ mod tests { m.submit_hashrate(U256::from(15), H256::from(1)); m.submit_hashrate(U256::from(20), H256::from(2)); - // then assert_eq!(m.hashrate(), U256::from(35)); } diff --git a/miner/src/gas_pricer.rs b/miner/src/gas_pricer.rs new file mode 100644 index 0000000000000000000000000000000000000000..ecb69ba572eb097d3b575d2108a6364714c43bc0 --- /dev/null +++ b/miner/src/gas_pricer.rs @@ -0,0 +1,97 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Auto-updates minimal gas price requirement. + +use std::time::{Instant, Duration}; + +use ansi_term::Colour; +use ethereum_types::U256; +use futures_cpupool::CpuPool; +use price_info::{Client as PriceInfoClient, PriceInfo}; +use price_info::fetch::Client as FetchClient; + +/// Options for the dynamic gas price recalibrator. +#[derive(Debug, PartialEq)] +pub struct GasPriceCalibratorOptions { + /// Base transaction price to match against. + pub usd_per_tx: f32, + /// How frequently we should recalibrate. + pub recalibration_period: Duration, +} + +/// The gas price validator variant for a `GasPricer`. +#[derive(Debug, PartialEq)] +pub struct GasPriceCalibrator { + options: GasPriceCalibratorOptions, + next_calibration: Instant, + price_info: PriceInfoClient, +} + +impl GasPriceCalibrator { + fn recalibrate(&mut self, set_price: F) { + trace!(target: "miner", "Recalibrating {:?} versus {:?}", Instant::now(), self.next_calibration); + if Instant::now() >= self.next_calibration { + let usd_per_tx = self.options.usd_per_tx; + trace!(target: "miner", "Getting price info"); + + self.price_info.get(move |price: PriceInfo| { + trace!(target: "miner", "Price info arrived: {:?}", price); + let usd_per_eth = price.ethusd; + let wei_per_usd: f32 = 1.0e18 / usd_per_eth; + let gas_per_tx: f32 = 21000.0; + let wei_per_gas: f32 = wei_per_usd * usd_per_tx / gas_per_tx; + info!(target: "miner", "Updated conversion rate to Ξ1 = {} ({} wei/gas)", Colour::White.bold().paint(format!("US${:.2}", usd_per_eth)), Colour::Yellow.bold().paint(format!("{}", wei_per_gas))); + set_price(U256::from(wei_per_gas as u64)); + }); + + self.next_calibration = Instant::now() + self.options.recalibration_period; + } + } +} + +/// Struct to look after updating the acceptable gas price of a miner. +#[derive(Debug, PartialEq)] +pub enum GasPricer { + /// A fixed gas price in terms of Wei - always the argument given. + Fixed(U256), + /// Gas price is calibrated according to a fixed amount of USD. + Calibrated(GasPriceCalibrator), +} + +impl GasPricer { + /// Create a new Calibrated `GasPricer`. + pub fn new_calibrated(options: GasPriceCalibratorOptions, fetch: FetchClient, p: CpuPool) -> GasPricer { + GasPricer::Calibrated(GasPriceCalibrator { + options: options, + next_calibration: Instant::now(), + price_info: PriceInfoClient::new(fetch, p), + }) + } + + /// Create a new Fixed `GasPricer`. + pub fn new_fixed(gas_price: U256) -> GasPricer { + GasPricer::Fixed(gas_price) + } + + /// Recalibrate current gas price. + pub fn recalibrate(&mut self, set_price: F) { + match *self { + GasPricer::Fixed(ref max) => set_price(max.clone()), + GasPricer::Calibrated(ref mut cal) => cal.recalibrate(set_price), + } + } +} diff --git a/miner/src/lib.rs b/miner/src/lib.rs index e8a86bd02a4aa95b5a83c8771ab5c6270f588eac..6b61df4de4e40fe879b05a82023f11839ab3d049 100644 --- a/miner/src/lib.rs +++ b/miner/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -19,27 +19,34 @@ //! Miner module //! Keeps track of transactions and mined block. -extern crate common_types as types; -extern crate ethabi; +extern crate ansi_term; extern crate ethcore_transaction as transaction; extern crate ethereum_types; extern crate futures; +extern crate futures_cpupool; extern crate heapsize; extern crate keccak_hash as hash; extern crate linked_hash_map; extern crate parking_lot; -extern crate table; -extern crate transient_hashmap; +extern crate price_info; +extern crate rlp; +extern crate transaction_pool as txpool; -#[cfg(test)] -extern crate ethkey; +#[macro_use] +extern crate error_chain; #[macro_use] extern crate log; +#[macro_use] +extern crate trace_time; + #[cfg(test)] extern crate rustc_hex; +#[cfg(test)] +extern crate ethkey; +#[cfg(test)] +extern crate env_logger; -pub mod banning_queue; pub mod external; -pub mod local_transactions; -pub mod transaction_queue; +pub mod gas_pricer; +pub mod pool; pub mod work_notify; diff --git a/miner/src/local_transactions.rs b/miner/src/local_transactions.rs deleted file mode 100644 index e8d1988a4f670c928445ce39748f87482de3f552..0000000000000000000000000000000000000000 --- a/miner/src/local_transactions.rs +++ /dev/null @@ -1,220 +0,0 @@ -// Copyright 2015-2017 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 . - -//! Local Transactions List. - -use ethereum_types::{H256, U256}; -use linked_hash_map::LinkedHashMap; -use transaction::{self, SignedTransaction, PendingTransaction}; - -/// Status of local transaction. -/// Can indicate that the transaction is currently part of the queue (`Pending/Future`) -/// or gives a reason why the transaction was removed. -#[derive(Debug, PartialEq, Clone)] -pub enum Status { - /// The transaction is currently in the transaction queue. - Pending, - /// The transaction is in future part of the queue. - Future, - /// Transaction is already mined. - Mined(SignedTransaction), - /// Transaction is dropped because of limit - Dropped(SignedTransaction), - /// Replaced because of higher gas price of another transaction. - Replaced(SignedTransaction, U256, H256), - /// Transaction was never accepted to the queue. - Rejected(SignedTransaction, transaction::Error), - /// Transaction is invalid. - Invalid(SignedTransaction), - /// Transaction was canceled. - Canceled(PendingTransaction), -} - -impl Status { - fn is_current(&self) -> bool { - *self == Status::Pending || *self == Status::Future - } -} - -/// Keeps track of local transactions that are in the queue or were mined/dropped recently. -#[derive(Debug)] -pub struct LocalTransactionsList { - max_old: usize, - transactions: LinkedHashMap, -} - -impl Default for LocalTransactionsList { - fn default() -> Self { - Self::new(10) - } -} - -impl LocalTransactionsList { - /// Create a new list of local transactions. - pub fn new(max_old: usize) -> Self { - LocalTransactionsList { - max_old: max_old, - transactions: Default::default(), - } - } - - /// Mark transaction with given hash as pending. - pub fn mark_pending(&mut self, hash: H256) { - debug!(target: "own_tx", "Imported to Current (hash {:?})", hash); - self.clear_old(); - self.transactions.insert(hash, Status::Pending); - } - - /// Mark transaction with given hash as future. - pub fn mark_future(&mut self, hash: H256) { - debug!(target: "own_tx", "Imported to Future (hash {:?})", hash); - self.transactions.insert(hash, Status::Future); - self.clear_old(); - } - - /// Mark given transaction as rejected from the queue. - pub fn mark_rejected(&mut self, tx: SignedTransaction, err: transaction::Error) { - debug!(target: "own_tx", "Transaction rejected (hash {:?}): {:?}", tx.hash(), err); - self.transactions.insert(tx.hash(), Status::Rejected(tx, err)); - self.clear_old(); - } - - /// Mark the transaction as replaced by transaction with given hash. - pub fn mark_replaced(&mut self, tx: SignedTransaction, gas_price: U256, hash: H256) { - debug!(target: "own_tx", "Transaction replaced (hash {:?}) by {:?} (new gas price: {:?})", tx.hash(), hash, gas_price); - self.transactions.insert(tx.hash(), Status::Replaced(tx, gas_price, hash)); - self.clear_old(); - } - - /// Mark transaction as invalid. - pub fn mark_invalid(&mut self, tx: SignedTransaction) { - warn!(target: "own_tx", "Transaction marked invalid (hash {:?})", tx.hash()); - self.transactions.insert(tx.hash(), Status::Invalid(tx)); - self.clear_old(); - } - - /// Mark transaction as canceled. - pub fn mark_canceled(&mut self, tx: PendingTransaction) { - warn!(target: "own_tx", "Transaction canceled (hash {:?})", tx.hash()); - self.transactions.insert(tx.hash(), Status::Canceled(tx)); - self.clear_old(); - } - - /// Mark transaction as dropped because of limit. - pub fn mark_dropped(&mut self, tx: SignedTransaction) { - warn!(target: "own_tx", "Transaction dropped (hash {:?})", tx.hash()); - self.transactions.insert(tx.hash(), Status::Dropped(tx)); - self.clear_old(); - } - - /// Mark transaction as mined. - pub fn mark_mined(&mut self, tx: SignedTransaction) { - info!(target: "own_tx", "Transaction mined (hash {:?})", tx.hash()); - self.transactions.insert(tx.hash(), Status::Mined(tx)); - self.clear_old(); - } - - /// Returns true if the transaction is already in local transactions. - pub fn contains(&self, hash: &H256) -> bool { - self.transactions.contains_key(hash) - } - - /// Return a map of all currently stored transactions. - pub fn all_transactions(&self) -> &LinkedHashMap { - &self.transactions - } - - fn clear_old(&mut self) { - let number_of_old = self.transactions - .values() - .filter(|status| !status.is_current()) - .count(); - - if self.max_old >= number_of_old { - return; - } - - let to_remove = self.transactions - .iter() - .filter(|&(_, status)| !status.is_current()) - .map(|(hash, _)| *hash) - .take(number_of_old - self.max_old) - .collect::>(); - - for hash in to_remove { - self.transactions.remove(&hash); - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use ethereum_types::U256; - use ethkey::{Random, Generator}; - - #[test] - fn should_add_transaction_as_pending() { - // given - let mut list = LocalTransactionsList::default(); - - // when - list.mark_pending(10.into()); - list.mark_future(20.into()); - - // then - assert!(list.contains(&10.into()), "Should contain the transaction."); - assert!(list.contains(&20.into()), "Should contain the transaction."); - let statuses = list.all_transactions().values().cloned().collect::>(); - assert_eq!(statuses, vec![Status::Pending, Status::Future]); - } - - #[test] - fn should_clear_old_transactions() { - // given - let mut list = LocalTransactionsList::new(1); - let tx1 = new_tx(10.into()); - let tx1_hash = tx1.hash(); - let tx2 = new_tx(50.into()); - let tx2_hash = tx2.hash(); - - list.mark_pending(10.into()); - list.mark_invalid(tx1); - list.mark_dropped(tx2); - assert!(list.contains(&tx2_hash)); - assert!(!list.contains(&tx1_hash)); - assert!(list.contains(&10.into())); - - // when - list.mark_future(15.into()); - - // then - assert!(list.contains(&10.into())); - assert!(list.contains(&15.into())); - } - - fn new_tx(nonce: U256) -> SignedTransaction { - let keypair = Random.generate().unwrap(); - transaction::Transaction { - action: transaction::Action::Create, - value: U256::from(100), - data: Default::default(), - gas: U256::from(10), - gas_price: U256::from(1245), - nonce: nonce - }.sign(keypair.secret(), None) - } -} diff --git a/miner/src/pool/client.rs b/miner/src/pool/client.rs new file mode 100644 index 0000000000000000000000000000000000000000..bdf57312eecc8cdb801765788ea0837e304e6eb2 --- /dev/null +++ b/miner/src/pool/client.rs @@ -0,0 +1,75 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Transaction Pool state client. +//! +//! `Client` encapsulates all external data required for the verifaction and readiness. +//! It includes any Ethereum state parts required for checking the transaction and +//! any consensus-required structure of the transaction. + +use std::fmt; + +use ethereum_types::{U256, H256, H160 as Address}; +use transaction; + +/// Account Details +#[derive(Debug, Clone)] +pub struct AccountDetails { + /// Current account nonce + pub nonce: U256, + /// Current account balance + pub balance: U256, + /// Is this account a local account? + pub is_local: bool, +} + +/// Transaction type +#[derive(Debug, PartialEq)] +pub enum TransactionType { + /// Regular transaction + Regular, + /// Service transaction (allowed by a contract to have gas_price=0) + Service, +} + +/// Verification client. +pub trait Client: fmt::Debug + Sync { + /// Is transaction with given hash already in the blockchain? + fn transaction_already_included(&self, hash: &H256) -> bool; + + /// Structurarily verify given transaction. + fn verify_transaction(&self, tx: transaction::UnverifiedTransaction) + -> Result; + + /// Estimate minimal gas requirurement for given transaction. + fn required_gas(&self, tx: &transaction::Transaction) -> U256; + + /// Fetch account details for given sender. + fn account_details(&self, address: &Address) -> AccountDetails; + + /// Classify transaction (check if transaction is filtered by some contracts). + fn transaction_type(&self, tx: &transaction::SignedTransaction) -> TransactionType; + + /// Performs pre-validation of RLP decoded transaction + fn decode_transaction(&self, transaction: &[u8]) + -> Result; +} + +/// State nonce client +pub trait NonceClient: fmt::Debug + Sync { + /// Fetch only account nonce for given sender. + fn account_nonce(&self, address: &Address) -> U256; +} diff --git a/miner/src/pool/listener.rs b/miner/src/pool/listener.rs new file mode 100644 index 0000000000000000000000000000000000000000..e881a2ba29d9cf84043eec69f656f0c1a7269ac7 --- /dev/null +++ b/miner/src/pool/listener.rs @@ -0,0 +1,159 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Notifier for new transaction hashes. + +use std::fmt; +use std::sync::Arc; + +use ethereum_types::H256; +use txpool::{self, VerifiedTransaction}; + +use pool::VerifiedTransaction as Transaction; + +type Listener = Box; + +/// Manages notifications to pending transaction listeners. +#[derive(Default)] +pub struct Notifier { + listeners: Vec, + pending: Vec, +} + +impl fmt::Debug for Notifier { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_struct("Notifier") + .field("listeners", &self.listeners.len()) + .field("pending", &self.pending) + .finish() + } +} + +impl Notifier { + /// Add new listener to receive notifications. + pub fn add(&mut self, f: Listener) { + self.listeners.push(f) + } + + /// Notify listeners about all currently pending transactions. + pub fn notify(&mut self) { + for l in &self.listeners { + (l)(&self.pending); + } + + self.pending.clear(); + } +} + +impl txpool::Listener for Notifier { + fn added(&mut self, tx: &Arc, _old: Option<&Arc>) { + self.pending.push(*tx.hash()); + } +} + +/// Transaction pool logger. +#[derive(Default, Debug)] +pub struct Logger; + +impl txpool::Listener for Logger { + fn added(&mut self, tx: &Arc, old: Option<&Arc>) { + debug!(target: "txqueue", "[{:?}] Added to the pool.", tx.hash()); + debug!( + target: "txqueue", + "[{hash:?}] Sender: {sender}, nonce: {nonce}, gasPrice: {gas_price}, gas: {gas}, value: {value}, dataLen: {data}))", + hash = tx.hash(), + sender = tx.sender(), + nonce = tx.signed().nonce, + gas_price = tx.signed().gas_price, + gas = tx.signed().gas, + value = tx.signed().value, + data = tx.signed().data.len(), + ); + + if let Some(old) = old { + debug!(target: "txqueue", "[{:?}] Dropped. Replaced by [{:?}]", old.hash(), tx.hash()); + } + } + + fn rejected(&mut self, _tx: &Arc, reason: &txpool::ErrorKind) { + trace!(target: "txqueue", "Rejected {}.", reason); + } + + fn dropped(&mut self, tx: &Arc, new: Option<&Transaction>) { + match new { + Some(new) => debug!(target: "txqueue", "[{:?}] Pushed out by [{:?}]", tx.hash(), new.hash()), + None => debug!(target: "txqueue", "[{:?}] Dropped.", tx.hash()), + } + } + + fn invalid(&mut self, tx: &Arc) { + debug!(target: "txqueue", "[{:?}] Marked as invalid by executor.", tx.hash()); + } + + fn canceled(&mut self, tx: &Arc) { + debug!(target: "txqueue", "[{:?}] Canceled by the user.", tx.hash()); + } + + fn mined(&mut self, tx: &Arc) { + debug!(target: "txqueue", "[{:?}] Mined.", tx.hash()); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use parking_lot::Mutex; + use transaction; + use txpool::Listener; + + #[test] + fn should_notify_listeners() { + // given + let received = Arc::new(Mutex::new(vec![])); + let r = received.clone(); + let listener = Box::new(move |hashes: &[H256]| { + *r.lock() = hashes.iter().map(|x| *x).collect(); + }); + + let mut tx_listener = Notifier::default(); + tx_listener.add(listener); + + // when + let tx = new_tx(); + tx_listener.added(&tx, None); + assert_eq!(*received.lock(), vec![]); + + // then + tx_listener.notify(); + assert_eq!( + *received.lock(), + vec!["13aff4201ac1dc49daf6a7cf07b558ed956511acbaabf9502bdacc353953766d".parse().unwrap()] + ); + } + + fn new_tx() -> Arc { + let signed = transaction::Transaction { + action: transaction::Action::Create, + data: vec![1, 2, 3], + nonce: 5.into(), + gas: 21_000.into(), + gas_price: 5.into(), + value: 0.into(), + }.fake_sign(5.into()); + + Arc::new(Transaction::from_pending_block_transaction(signed)) + } +} diff --git a/miner/src/pool/local_transactions.rs b/miner/src/pool/local_transactions.rs new file mode 100644 index 0000000000000000000000000000000000000000..d69da3347acfd49ee6404778236ede98d2c6dbb0 --- /dev/null +++ b/miner/src/pool/local_transactions.rs @@ -0,0 +1,272 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Local Transactions List. + +use std::sync::Arc; + +use ethereum_types::H256; +use linked_hash_map::LinkedHashMap; +use pool::VerifiedTransaction as Transaction; +use txpool::{self, VerifiedTransaction}; + +/// Status of local transaction. +/// Can indicate that the transaction is currently part of the queue (`Pending/Future`) +/// or gives a reason why the transaction was removed. +#[derive(Debug, PartialEq, Clone)] +pub enum Status { + /// The transaction is currently in the transaction queue. + Pending(Arc), + /// Transaction is already mined. + Mined(Arc), + /// Transaction is dropped because of limit + Dropped(Arc), + /// Replaced because of higher gas price of another transaction. + Replaced { + /// Replaced transaction + old: Arc, + /// Transaction that replaced this one. + new: Arc, + }, + /// Transaction was never accepted to the queue. + /// It means that it was too cheap to replace any transaction already in the pool. + Rejected(Arc, String), + /// Transaction is invalid. + Invalid(Arc), + /// Transaction was canceled. + Canceled(Arc), +} + +impl Status { + fn is_pending(&self) -> bool { + match *self { + Status::Pending(_) => true, + _ => false, + } + } +} + +/// Keeps track of local transactions that are in the queue or were mined/dropped recently. +#[derive(Debug)] +pub struct LocalTransactionsList { + max_old: usize, + transactions: LinkedHashMap, + pending: usize, +} + +impl Default for LocalTransactionsList { + fn default() -> Self { + Self::new(10) + } +} + +impl LocalTransactionsList { + /// Create a new list of local transactions. + pub fn new(max_old: usize) -> Self { + LocalTransactionsList { + max_old, + transactions: Default::default(), + pending: 0, + } + } + + /// Returns true if the transaction is already in local transactions. + pub fn contains(&self, hash: &H256) -> bool { + self.transactions.contains_key(hash) + } + + /// Return a map of all currently stored transactions. + pub fn all_transactions(&self) -> &LinkedHashMap { + &self.transactions + } + + /// Returns true if there are pending local transactions. + pub fn has_pending(&self) -> bool { + self.pending > 0 + } + + fn clear_old(&mut self) { + let number_of_old = self.transactions.len() - self.pending; + if self.max_old >= number_of_old { + return; + } + + let to_remove: Vec<_> = self.transactions + .iter() + .filter(|&(_, status)| !status.is_pending()) + .map(|(hash, _)| *hash) + .take(number_of_old - self.max_old) + .collect(); + + for hash in to_remove { + self.transactions.remove(&hash); + } + } + + fn insert(&mut self, hash: H256, status: Status) { + let result = self.transactions.insert(hash, status); + if let Some(old) = result { + if old.is_pending() { + self.pending -= 1; + } + } + } +} + +impl txpool::Listener for LocalTransactionsList { + fn added(&mut self, tx: &Arc, old: Option<&Arc>) { + if !tx.priority().is_local() { + return; + } + + debug!(target: "own_tx", "Imported to the pool (hash {:?})", tx.hash()); + self.clear_old(); + self.insert(*tx.hash(), Status::Pending(tx.clone())); + self.pending += 1; + + if let Some(old) = old { + if self.transactions.contains_key(old.hash()) { + self.insert(*old.hash(), Status::Replaced { + old: old.clone(), + new: tx.clone(), + }); + } + } + } + + fn rejected(&mut self, tx: &Arc, reason: &txpool::ErrorKind) { + if !tx.priority().is_local() { + return; + } + + debug!(target: "own_tx", "Transaction rejected (hash {:?}). {}", tx.hash(), reason); + self.insert(*tx.hash(), Status::Rejected(tx.clone(), format!("{}", reason))); + self.clear_old(); + } + + fn dropped(&mut self, tx: &Arc, new: Option<&Transaction>) { + if !tx.priority().is_local() { + return; + } + + match new { + Some(new) => warn!(target: "own_tx", "Transaction pushed out because of limit (hash {:?}, replacement: {:?})", tx.hash(), new.hash()), + None => warn!(target: "own_tx", "Transaction dropped because of limit (hash: {:?})", tx.hash()), + } + self.insert(*tx.hash(), Status::Dropped(tx.clone())); + self.clear_old(); + } + + fn invalid(&mut self, tx: &Arc) { + if !tx.priority().is_local() { + return; + } + + warn!(target: "own_tx", "Transaction marked invalid (hash {:?})", tx.hash()); + self.insert(*tx.hash(), Status::Invalid(tx.clone())); + self.clear_old(); + } + + fn canceled(&mut self, tx: &Arc) { + if !tx.priority().is_local() { + return; + } + + warn!(target: "own_tx", "Transaction canceled (hash {:?})", tx.hash()); + self.insert(*tx.hash(), Status::Canceled(tx.clone())); + self.clear_old(); + } + + /// The transaction has been mined. + fn mined(&mut self, tx: &Arc) { + if !tx.priority().is_local() { + return; + } + + info!(target: "own_tx", "Transaction mined (hash {:?})", tx.hash()); + self.insert(*tx.hash(), Status::Mined(tx.clone())); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use ethereum_types::U256; + use ethkey::{Random, Generator}; + use transaction; + use txpool::Listener; + + use pool; + + #[test] + fn should_add_transaction_as_pending() { + // given + let mut list = LocalTransactionsList::default(); + let tx1 = new_tx(10); + let tx2 = new_tx(20); + + // when + list.added(&tx1, None); + list.added(&tx2, None); + + // then + assert!(list.contains(tx1.hash())); + assert!(list.contains(tx2.hash())); + let statuses = list.all_transactions().values().cloned().collect::>(); + assert_eq!(statuses, vec![Status::Pending(tx1), Status::Pending(tx2)]); + } + + #[test] + fn should_clear_old_transactions() { + // given + let mut list = LocalTransactionsList::new(1); + let tx1 = new_tx(10); + let tx2 = new_tx(50); + let tx3 = new_tx(51); + + list.added(&tx1, None); + list.invalid(&tx1); + list.dropped(&tx2, None); + assert!(!list.contains(tx1.hash())); + assert!(list.contains(tx2.hash())); + assert!(!list.contains(tx3.hash())); + + // when + list.added(&tx3, Some(&tx1)); + + // then + assert!(!list.contains(tx1.hash())); + assert!(list.contains(tx2.hash())); + assert!(list.contains(tx3.hash())); + } + + fn new_tx>(nonce: T) -> Arc { + let keypair = Random.generate().unwrap(); + let signed = transaction::Transaction { + action: transaction::Action::Create, + value: U256::from(100), + data: Default::default(), + gas: U256::from(10), + gas_price: U256::from(1245), + nonce: nonce.into(), + }.sign(keypair.secret(), None); + + let mut tx = Transaction::from_pending_block_transaction(signed); + tx.priority = pool::Priority::Local; + + Arc::new(tx) + } +} diff --git a/miner/src/pool/mod.rs b/miner/src/pool/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..fd4ef6ef2e811f7e8b7726ca99a4a668ef8a080b --- /dev/null +++ b/miner/src/pool/mod.rs @@ -0,0 +1,177 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Transaction Pool + +use ethereum_types::{U256, H256, Address}; +use heapsize::HeapSizeOf; +use transaction; +use txpool; + +mod listener; +mod queue; +mod ready; +mod scoring; + +pub mod client; +pub mod local_transactions; +pub mod verifier; + +#[cfg(test)] +mod tests; + +pub use self::queue::{TransactionQueue, Status as QueueStatus}; +pub use self::txpool::{VerifiedTransaction as PoolVerifiedTransaction, Options}; + +/// How to prioritize transactions in the pool +/// +/// TODO [ToDr] Implement more strategies. +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum PrioritizationStrategy { + /// Simple gas-price based prioritization. + GasPriceOnly, +} + +/// Transaction ordering when requesting pending set. +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum PendingOrdering { + /// Get pending transactions ordered by their priority (potentially expensive) + Priority, + /// Get pending transactions without any care of particular ordering (cheaper). + Unordered, +} + +/// Pending set query settings +#[derive(Debug, Clone)] +pub struct PendingSettings { + /// Current block number (affects readiness of some transactions). + pub block_number: u64, + /// Current timestamp (affects readiness of some transactions). + pub current_timestamp: u64, + /// Nonce cap (for dust protection; EIP-168) + pub nonce_cap: Option, + /// Maximal number of transactions in pending the set. + pub max_len: usize, + /// Ordering of transactions. + pub ordering: PendingOrdering, +} + +impl PendingSettings { + /// Get all transactions (no cap or len limit) prioritized. + pub fn all_prioritized(block_number: u64, current_timestamp: u64) -> Self { + PendingSettings { + block_number, + current_timestamp, + nonce_cap: None, + max_len: usize::max_value(), + ordering: PendingOrdering::Priority, + } + } +} + +/// Transaction priority. +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub(crate) enum Priority { + /// Local transactions (high priority) + /// + /// Transactions either from a local account or + /// submitted over local RPC connection via `eth_sendRawTransaction` + Local, + /// Transactions from retracted blocks (medium priority) + /// + /// When block becomes non-canonical we re-import the transactions it contains + /// to the queue and boost their priority. + Retracted, + /// Regular transactions received over the network. (no priority boost) + Regular, +} + +impl Priority { + fn is_local(&self) -> bool { + match *self { + Priority::Local => true, + _ => false, + } + } +} + +/// Verified transaction stored in the pool. +#[derive(Debug, PartialEq, Eq)] +pub struct VerifiedTransaction { + transaction: transaction::PendingTransaction, + // TODO [ToDr] hash and sender should go directly from the transaction + hash: H256, + sender: Address, + priority: Priority, + insertion_id: usize, +} + +impl VerifiedTransaction { + /// Create `VerifiedTransaction` directly from `SignedTransaction`. + /// + /// This method should be used only: + /// 1. for tests + /// 2. In case we are converting pending block transactions that are already in the queue to match the function signature. + pub fn from_pending_block_transaction(tx: transaction::SignedTransaction) -> Self { + let hash = tx.hash(); + let sender = tx.sender(); + VerifiedTransaction { + transaction: tx.into(), + hash, + sender, + priority: Priority::Retracted, + insertion_id: 0, + } + } + + /// Gets transaction priority. + pub(crate) fn priority(&self) -> Priority { + self.priority + } + + /// Gets transaction insertion id. + pub(crate) fn insertion_id(&self) -> usize { + self.insertion_id + } + + /// Gets wrapped `SignedTransaction` + pub fn signed(&self) -> &transaction::SignedTransaction { + &self.transaction + } + + /// Gets wrapped `PendingTransaction` + pub fn pending(&self) -> &transaction::PendingTransaction { + &self.transaction + } + +} + +impl txpool::VerifiedTransaction for VerifiedTransaction { + type Hash = H256; + type Sender = Address; + + fn hash(&self) -> &H256 { + &self.hash + } + + fn mem_usage(&self) -> usize { + self.transaction.heap_size_of_children() + } + + fn sender(&self) -> &Address { + &self.sender + } +} diff --git a/miner/src/pool/queue.rs b/miner/src/pool/queue.rs new file mode 100644 index 0000000000000000000000000000000000000000..4351087b29b6cd4e5db94f12953d6ba31dcba98e --- /dev/null +++ b/miner/src/pool/queue.rs @@ -0,0 +1,486 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Ethereum Transaction Queue + +use std::{cmp, fmt}; +use std::sync::Arc; +use std::sync::atomic::{self, AtomicUsize}; +use std::collections::BTreeMap; + +use ethereum_types::{H256, U256, Address}; +use parking_lot::RwLock; +use transaction; +use txpool::{self, Verifier}; + +use pool::{ + self, scoring, verifier, client, ready, listener, + PrioritizationStrategy, PendingOrdering, PendingSettings, +}; +use pool::local_transactions::LocalTransactionsList; + +type Listener = (LocalTransactionsList, (listener::Notifier, listener::Logger)); +type Pool = txpool::Pool; + +/// Max cache time in milliseconds for pending transactions. +/// +/// Pending transactions are cached and will only be computed again +/// if last cache has been created earler than `TIMESTAMP_CACHE` ms ago. +/// This timeout applies only if there are local pending transactions +/// since it only affects transaction Condition. +const TIMESTAMP_CACHE: u64 = 1000; + +/// Transaction queue status. +#[derive(Debug, Clone, PartialEq)] +pub struct Status { + /// Verifier options. + pub options: verifier::Options, + /// Current status of the transaction pool. + pub status: txpool::LightStatus, + /// Current limits of the transaction pool. + pub limits: txpool::Options, +} + +impl fmt::Display for Status { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + writeln!( + fmt, + "Pool: {current}/{max} ({senders} senders; {mem}/{mem_max} kB) [minGasPrice: {gp} Mwei, maxGas: {max_gas}]", + current = self.status.transaction_count, + max = self.limits.max_count, + senders = self.status.senders, + mem = self.status.mem_usage / 1024, + mem_max = self.limits.max_mem_usage / 1024, + gp = self.options.minimal_gas_price / 1_000_000.into(), + max_gas = cmp::min(self.options.block_gas_limit, self.options.tx_gas_limit), + ) + } +} + +#[derive(Debug)] +struct CachedPending { + block_number: u64, + current_timestamp: u64, + nonce_cap: Option, + has_local_pending: bool, + pending: Option>>, + max_len: usize, +} + +impl CachedPending { + /// Creates new `CachedPending` without cached set. + pub fn none() -> Self { + CachedPending { + block_number: 0, + current_timestamp: 0, + has_local_pending: false, + pending: None, + nonce_cap: None, + max_len: 0, + } + } + + /// Remove cached pending set. + pub fn clear(&mut self) { + self.pending = None; + } + + /// Returns cached pending set (if any) if it's valid. + pub fn pending( + &self, + block_number: u64, + current_timestamp: u64, + nonce_cap: Option<&U256>, + max_len: usize, + ) -> Option>> { + // First check if we have anything in cache. + let pending = self.pending.as_ref()?; + + if block_number != self.block_number { + return None; + } + + // In case we don't have any local pending transactions + // there is no need to invalidate the cache because of timestamp. + // Timestamp only affects local `PendingTransactions` with `Condition::Timestamp`. + if self.has_local_pending && current_timestamp > self.current_timestamp + TIMESTAMP_CACHE { + return None; + } + + // It's fine to return limited set even if `nonce_cap` is `None`. + // The worst thing that may happen is that some transactions won't get propagated in current round, + // but they are not really valid in current block anyway. We will propagate them in the next round. + // Also there is no way to have both `Some` with different numbers since it depends on the block number + // and a constant parameter in schedule (`nonce_cap_increment`) + if self.nonce_cap.is_none() && nonce_cap.is_some() { + return None; + } + + // It's fine to just take a smaller subset, but not other way around. + if max_len > self.max_len { + return None; + } + + Some(pending.iter().take(max_len).cloned().collect()) + } +} + +/// Ethereum Transaction Queue +/// +/// Responsible for: +/// - verifying incoming transactions +/// - maintaining a pool of verified transactions. +/// - returning an iterator for transactions that are ready to be included in block (pending) +#[derive(Debug)] +pub struct TransactionQueue { + insertion_id: Arc, + pool: RwLock, + options: RwLock, + cached_pending: RwLock, +} + +impl TransactionQueue { + /// Create new queue with given pool limits and initial verification options. + pub fn new( + limits: txpool::Options, + verification_options: verifier::Options, + strategy: PrioritizationStrategy, + ) -> Self { + TransactionQueue { + insertion_id: Default::default(), + pool: RwLock::new(txpool::Pool::new(Default::default(), scoring::NonceAndGasPrice(strategy), limits)), + options: RwLock::new(verification_options), + cached_pending: RwLock::new(CachedPending::none()), + } + } + + /// Update verification options + /// + /// Some parameters of verification may vary in time (like block gas limit or minimal gas price). + pub fn set_verifier_options(&self, options: verifier::Options) { + *self.options.write() = options; + } + + /// Import a set of transactions to the pool. + /// + /// Given blockchain and state access (Client) + /// verifies and imports transactions to the pool. + pub fn import( + &self, + client: C, + transactions: Vec, + ) -> Vec> { + // Run verification + trace_time!("pool::verify_and_import"); + let options = self.options.read().clone(); + + let verifier = verifier::Verifier::new(client, options, self.insertion_id.clone()); + let results = transactions + .into_iter() + .map(|transaction| { + if self.pool.read().find(&transaction.hash()).is_some() { + bail!(transaction::Error::AlreadyImported) + } + + verifier.verify_transaction(transaction) + }) + .map(|result| result.and_then(|verified| { + self.pool.write().import(verified) + .map(|_imported| ()) + .map_err(convert_error) + })) + .collect::>(); + + // Notify about imported transactions. + (self.pool.write().listener_mut().1).0.notify(); + + if results.iter().any(|r| r.is_ok()) { + self.cached_pending.write().clear(); + } + + results + } + + /// Returns all transactions in the queue without explicit ordering. + pub fn all_transactions(&self) -> Vec> { + let ready = |_tx: &pool::VerifiedTransaction| txpool::Readiness::Ready; + self.pool.read().unordered_pending(ready).collect() + } + + /// Returns current pending transactions ordered by priority. + /// + /// NOTE: This may return a cached version of pending transaction set. + /// Re-computing the pending set is possible with `#collect_pending` method, + /// but be aware that it's a pretty expensive operation. + pub fn pending( + &self, + client: C, + settings: PendingSettings, + ) -> Vec> where + C: client::NonceClient, + { + let PendingSettings { block_number, current_timestamp, nonce_cap, max_len, ordering } = settings; + if let Some(pending) = self.cached_pending.read().pending(block_number, current_timestamp, nonce_cap.as_ref(), max_len) { + return pending; + } + + // Double check after acquiring write lock + let mut cached_pending = self.cached_pending.write(); + if let Some(pending) = cached_pending.pending(block_number, current_timestamp, nonce_cap.as_ref(), max_len) { + return pending; + } + + // In case we don't have a cached set, but we don't care about order + // just return the unordered set. + if let PendingOrdering::Unordered = ordering { + let ready = Self::ready(client, block_number, current_timestamp, nonce_cap); + return self.pool.read().unordered_pending(ready).take(max_len).collect(); + } + + let pending: Vec<_> = self.collect_pending(client, block_number, current_timestamp, nonce_cap, |i| { + i.take(max_len).collect() + }); + + *cached_pending = CachedPending { + block_number, + current_timestamp, + nonce_cap, + has_local_pending: self.has_local_pending_transactions(), + pending: Some(pending.clone()), + max_len, + }; + + pending + } + + /// Collect pending transactions. + /// + /// NOTE This is re-computing the pending set and it might be expensive to do so. + /// Prefer using cached pending set using `#pending` method. + pub fn collect_pending( + &self, + client: C, + block_number: u64, + current_timestamp: u64, + nonce_cap: Option, + collect: F, + ) -> T where + C: client::NonceClient, + F: FnOnce(txpool::PendingIterator< + pool::VerifiedTransaction, + (ready::Condition, ready::State), + scoring::NonceAndGasPrice, + Listener, + >) -> T, + { + debug!(target: "txqueue", "Re-computing pending set for block: {}", block_number); + trace_time!("pool::collect_pending"); + let ready = Self::ready(client, block_number, current_timestamp, nonce_cap); + collect(self.pool.read().pending(ready)) + } + + fn ready( + client: C, + block_number: u64, + current_timestamp: u64, + nonce_cap: Option, + ) -> (ready::Condition, ready::State) where + C: client::NonceClient, + { + let pending_readiness = ready::Condition::new(block_number, current_timestamp); + // don't mark any transactions as stale at this point. + let stale_id = None; + let state_readiness = ready::State::new(client, stale_id, nonce_cap); + + (pending_readiness, state_readiness) + } + + /// Culls all stalled transactions from the pool. + pub fn cull( + &self, + client: C, + ) { + // We don't care about future transactions, so nonce_cap is not important. + let nonce_cap = None; + // We want to clear stale transactions from the queue as well. + // (Transactions that are occuping the queue for a long time without being included) + let stale_id = { + let current_id = self.insertion_id.load(atomic::Ordering::Relaxed); + // wait at least for half of the queue to be replaced + let gap = self.pool.read().options().max_count / 2; + // but never less than 100 transactions + let gap = cmp::max(100, gap); + + current_id.checked_sub(gap) + }; + + let state_readiness = ready::State::new(client, stale_id, nonce_cap); + + let removed = self.pool.write().cull(None, state_readiness); + debug!(target: "txqueue", "Removed {} stalled transactions. {}", removed, self.status()); + } + + /// Returns next valid nonce for given sender + /// or `None` if there are no pending transactions from that sender. + pub fn next_nonce( + &self, + client: C, + address: &Address, + ) -> Option { + // Do not take nonce_cap into account when determining next nonce. + let nonce_cap = None; + // Also we ignore stale transactions in the queue. + let stale_id = None; + + let state_readiness = ready::State::new(client, stale_id, nonce_cap); + + self.pool.read().pending_from_sender(state_readiness, address) + .last() + .map(|tx| tx.signed().nonce + 1.into()) + } + + /// Retrieve a transaction from the pool. + /// + /// Given transaction hash looks up that transaction in the pool + /// and returns a shared pointer to it or `None` if it's not present. + pub fn find( + &self, + hash: &H256, + ) -> Option> { + self.pool.read().find(hash) + } + + /// Remove a set of transactions from the pool. + /// + /// Given an iterator of transaction hashes + /// removes them from the pool. + /// That method should be used if invalid transactions are detected + /// or you want to cancel a transaction. + pub fn remove<'a, T: IntoIterator>( + &self, + hashes: T, + is_invalid: bool, + ) -> Vec>> { + let results = { + let mut pool = self.pool.write(); + + hashes + .into_iter() + .map(|hash| pool.remove(hash, is_invalid)) + .collect::>() + }; + + if results.iter().any(Option::is_some) { + self.cached_pending.write().clear(); + } + + results + } + + /// Clear the entire pool. + pub fn clear(&self) { + self.pool.write().clear(); + } + + /// Penalize given senders. + pub fn penalize<'a, T: IntoIterator>(&self, senders: T) { + let mut pool = self.pool.write(); + for sender in senders { + pool.update_scores(sender, ()); + } + } + + /// Returns gas price of currently the worst transaction in the pool. + pub fn current_worst_gas_price(&self) -> U256 { + match self.pool.read().worst_transaction() { + Some(tx) => tx.signed().gas_price, + None => self.options.read().minimal_gas_price, + } + } + + /// Returns a status of the queue. + pub fn status(&self) -> Status { + let pool = self.pool.read(); + let status = pool.light_status(); + let limits = pool.options(); + let options = self.options.read().clone(); + + Status { + options, + status, + limits, + } + } + + /// Check if there are any local transactions in the pool. + /// + /// Returns `true` if there are any transactions in the pool + /// that has been marked as local. + /// + /// Local transactions are the ones from accounts managed by this node + /// and transactions submitted via local RPC (`eth_sendRawTransaction`) + pub fn has_local_pending_transactions(&self) -> bool { + self.pool.read().listener().0.has_pending() + } + + /// Returns status of recently seen local transactions. + pub fn local_transactions(&self) -> BTreeMap { + self.pool.read().listener().0.all_transactions().iter().map(|(a, b)| (*a, b.clone())).collect() + } + + /// Add a callback to be notified about all transactions entering the pool. + pub fn add_listener(&self, f: Box) { + let mut pool = self.pool.write(); + (pool.listener_mut().1).0.add(f); + } + + /// Check if pending set is cached. + #[cfg(test)] + pub fn is_pending_cached(&self) -> bool { + self.cached_pending.read().pending.is_some() + } +} + +fn convert_error(err: txpool::Error) -> transaction::Error { + use self::txpool::ErrorKind; + + match *err.kind() { + ErrorKind::AlreadyImported(..) => transaction::Error::AlreadyImported, + ErrorKind::TooCheapToEnter(..) => transaction::Error::LimitReached, + ErrorKind::TooCheapToReplace(..) => transaction::Error::TooCheapToReplace, + ref e => { + warn!(target: "txqueue", "Unknown import error: {:?}", e); + transaction::Error::NotAllowed + }, + } +} + +#[cfg(test)] +mod tests { + use super::*; + use pool::tests::client::TestClient; + + #[test] + fn should_get_pending_transactions() { + let queue = TransactionQueue::new(txpool::Options::default(), verifier::Options::default(), PrioritizationStrategy::GasPriceOnly); + + let pending: Vec<_> = queue.pending(TestClient::default(), PendingSettings::all_prioritized(0, 0)); + + for tx in pending { + assert!(tx.signed().nonce > 0.into()); + } + } +} diff --git a/miner/src/pool/ready.rs b/miner/src/pool/ready.rs new file mode 100644 index 0000000000000000000000000000000000000000..0b4d27f7f2c0863d72725695da2051daa3512d0f --- /dev/null +++ b/miner/src/pool/ready.rs @@ -0,0 +1,211 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Transaction Readiness indicator +//! +//! Transaction readiness is responsible for indicating if +//! particular transaction can be included in the block. +//! +//! Regular transactions are ready iff the current state nonce +//! (obtained from `NonceClient`) equals to the transaction nonce. +//! +//! Let's define `S = state nonce`. Transactions are processed +//! in order, so we first include transaction with nonce `S`, +//! but then we are able to include the one with `S + 1` nonce. +//! So bear in mind that transactions can be included in chains +//! and their readiness is dependent on previous transactions from +//! the same sender. +//! +//! There are three possible outcomes: +//! - The transaction is old (stalled; state nonce > transaction nonce) +//! - The transaction is ready (current; state nonce == transaction nonce) +//! - The transaction is not ready yet (future; state nonce < transaction nonce) +//! +//! NOTE The transactions are always checked for readines in order they are stored within the queue. +//! First `Readiness::Future` response also causes all subsequent transactions from the same sender +//! to be marked as `Future`. + +use std::cmp; +use std::collections::HashMap; + +use ethereum_types::{U256, H160 as Address}; +use transaction; +use txpool::{self, VerifiedTransaction as PoolVerifiedTransaction}; + +use super::client::NonceClient; +use super::VerifiedTransaction; + +/// Checks readiness of transactions by comparing the nonce to state nonce. +#[derive(Debug)] +pub struct State { + nonces: HashMap, + state: C, + max_nonce: Option, + stale_id: Option, +} + +impl State { + /// Create new State checker, given client interface. + pub fn new( + state: C, + stale_id: Option, + max_nonce: Option, + ) -> Self { + State { + nonces: Default::default(), + state, + max_nonce, + stale_id, + } + } +} + +impl txpool::Ready for State { + fn is_ready(&mut self, tx: &VerifiedTransaction) -> txpool::Readiness { + // Check max nonce + match self.max_nonce { + Some(nonce) if tx.transaction.nonce > nonce => { + return txpool::Readiness::Future; + }, + _ => {}, + } + + let sender = tx.sender(); + let state = &self.state; + let state_nonce = || state.account_nonce(sender); + let nonce = self.nonces.entry(*sender).or_insert_with(state_nonce); + match tx.transaction.nonce.cmp(nonce) { + // Before marking as future check for stale ids + cmp::Ordering::Greater => match self.stale_id { + Some(id) if tx.insertion_id() < id => txpool::Readiness::Stale, + _ => txpool::Readiness::Future, + }, + cmp::Ordering::Less => txpool::Readiness::Stale, + cmp::Ordering::Equal => { + *nonce = *nonce + 1.into(); + txpool::Readiness::Ready + }, + } + } +} + +/// Checks readines of Pending transactions by comparing it with current time and block number. +#[derive(Debug)] +pub struct Condition { + block_number: u64, + now: u64, +} + +impl Condition { + /// Create a new condition checker given current block number and UTC timestamp. + pub fn new(block_number: u64, now: u64) -> Self { + Condition { + block_number, + now, + } + } +} + +impl txpool::Ready for Condition { + fn is_ready(&mut self, tx: &VerifiedTransaction) -> txpool::Readiness { + match tx.transaction.condition { + Some(transaction::Condition::Number(block)) if block > self.block_number => txpool::Readiness::Future, + Some(transaction::Condition::Timestamp(time)) if time > self.now => txpool::Readiness::Future, + _ => txpool::Readiness::Ready, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use txpool::Ready; + use pool::tests::client::TestClient; + use pool::tests::tx::{Tx, TxExt}; + + #[test] + fn should_return_correct_state_readiness() { + // given + let (tx1, tx2, tx3) = Tx::default().signed_triple(); + let (tx1, tx2, tx3) = (tx1.verified(), tx2.verified(), tx3.verified()); + + // when + assert_eq!(State::new(TestClient::new(), None, None).is_ready(&tx3), txpool::Readiness::Future); + assert_eq!(State::new(TestClient::new(), None, None).is_ready(&tx2), txpool::Readiness::Future); + + let mut ready = State::new(TestClient::new(), None, None); + + // then + assert_eq!(ready.is_ready(&tx1), txpool::Readiness::Ready); + assert_eq!(ready.is_ready(&tx2), txpool::Readiness::Ready); + assert_eq!(ready.is_ready(&tx3), txpool::Readiness::Ready); + } + + #[test] + fn should_return_future_if_nonce_cap_reached() { + // given + let tx = Tx::default().signed().verified(); + + // when + let res1 = State::new(TestClient::new(), None, Some(10.into())).is_ready(&tx); + let res2 = State::new(TestClient::new(), None, Some(124.into())).is_ready(&tx); + + // then + assert_eq!(res1, txpool::Readiness::Future); + assert_eq!(res2, txpool::Readiness::Ready); + } + + #[test] + fn should_return_stale_if_nonce_does_not_match() { + // given + let tx = Tx::default().signed().verified(); + + // when + let res = State::new(TestClient::new().with_nonce(125), None, None).is_ready(&tx); + + // then + assert_eq!(res, txpool::Readiness::Stale); + } + + #[test] + fn should_return_stale_for_old_transactions() { + // given + let (_, tx) = Tx::default().signed_pair().verified(); + + // when + let res = State::new(TestClient::new(), Some(1), None).is_ready(&tx); + + // then + assert_eq!(res, txpool::Readiness::Stale); + } + + #[test] + fn should_check_readiness_of_condition() { + // given + let tx = Tx::default().signed(); + let v = |tx: transaction::PendingTransaction| TestClient::new().verify(tx); + let tx1 = v(transaction::PendingTransaction::new(tx.clone(), transaction::Condition::Number(5).into())); + let tx2 = v(transaction::PendingTransaction::new(tx.clone(), transaction::Condition::Timestamp(3).into())); + let tx3 = v(transaction::PendingTransaction::new(tx.clone(), None)); + + // when/then + assert_eq!(Condition::new(0, 0).is_ready(&tx1), txpool::Readiness::Future); + assert_eq!(Condition::new(0, 0).is_ready(&tx2), txpool::Readiness::Future); + assert_eq!(Condition::new(0, 0).is_ready(&tx3), txpool::Readiness::Ready); + assert_eq!(Condition::new(5, 0).is_ready(&tx1), txpool::Readiness::Ready); + assert_eq!(Condition::new(0, 3).is_ready(&tx2), txpool::Readiness::Ready); + } +} diff --git a/miner/src/pool/res/big_transaction.data b/miner/src/pool/res/big_transaction.data new file mode 100644 index 0000000000000000000000000000000000000000..15703f893ad08c5cb960542a5776c52b51a19c21 --- /dev/null +++ b/miner/src/pool/res/big_transaction.data @@ -0,0 +1 @@ +6060604052341561000f57600080fd5b604051610b0d380380610b0d833981016040528080518201919060200180518201919060200180518201919050508260009080519060200190610053929190610092565b50816002908051906020019061006a92919061011c565b50806001908051906020019061008192919061011c565b506001600381905550505050610204565b82805482825590600052602060002090810192821561010b579160200282015b8281111561010a5782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550916020019190600101906100b2565b5b509050610118919061019c565b5090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061015d57805160ff191683800117855561018b565b8280016001018555821561018b579182015b8281111561018a57825182559160200191906001019061016f565b5b50905061019891906101df565b5090565b6101dc91905b808211156101d857600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055506001016101a2565b5090565b90565b61020191905b808211156101fd5760008160009055506001016101e5565b5090565b90565b6108fa806102136000396000f300606060405260043610610078576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806317ac53a21461007d57806324c12bf61461019a57806335aa2e4414610228578063affed0e01461028b578063b7ab4db5146102b4578063c19d93fb1461031e575b600080fd5b341561008857600080fd5b610198600480803590602001908201803590602001908080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050919080359060200190820180359060200190808060200260200160405190810160405280939291908181526020018383602002808284378201915050505050509190803590602001908201803590602001908080602002602001604051908101604052809392919081815260200183836020028082843782019150505050505091908035906020019082018035906020019080806020026020016040519081016040528093929190818152602001838360200280828437820191505050505050919050506103ac565b005b34156101a557600080fd5b6101ad610600565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101ed5780820151818401526020810190506101d2565b50505050905090810190601f16801561021a5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561023357600080fd5b610249600480803590602001909190505061069e565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561029657600080fd5b61029e6106dd565b6040518082815260200191505060405180910390f35b34156102bf57600080fd5b6102c76106e3565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561030a5780820151818401526020810190506102ef565b505050509050019250505060405180910390f35b341561032957600080fd5b610331610777565b6040518080602001828103825283818151815260200191508051906020019080838360005b83811015610371578082015181840152602081019050610356565b50505050905090810190601f16801561039e5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6000806040805190810160405280876040518082805190602001908083835b6020831015156103f057805182526020820191506020810190506020830392506103cb565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390206000191660001916815260200160035460010260001916600019168152506040518082600260200280838360005b8381101561046657808201518184015260208101905061044b565b5050505090500191505060405180910390209150600090505b6000805490508110156105d55760008181548110151561049b57fe5b906000526020600020900160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660018387848151811015156104ee57fe5b90602001906020020151878581518110151561050657fe5b90602001906020020151878681518110151561051e57fe5b90602001906020020151604051600081526020016040526000604051602001526040518085600019166000191681526020018460ff1660ff16815260200183600019166000191681526020018260001916600019168152602001945050505050602060405160208103908084039060008661646e5a03f115156105a057600080fd5b50506020604051035173ffffffffffffffffffffffffffffffffffffffff161415156105c857fe5b808060010191505061047f565b85600190805190602001906105eb929190610815565b50600160035401600381905550505050505050565b60028054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156106965780601f1061066b57610100808354040283529160200191610696565b820191906000526020600020905b81548152906001019060200180831161067957829003601f168201915b505050505081565b6000818154811015156106ad57fe5b90600052602060002090016000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60035481565b6106eb610895565b600080548060200260200160405190810160405280929190818152602001828054801561076d57602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311610723575b5050505050905090565b60018054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561080d5780601f106107e25761010080835404028352916020019161080d565b820191906000526020600020905b8154815290600101906020018083116107f057829003601f168201915b505050505081565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061085657805160ff1916838001178555610884565b82800160010185558215610884579182015b82811115610883578251825591602001919060010190610868565b5b50905061089191906108a9565b5090565b602060405190810160405280600081525090565b6108cb91905b808211156108c75760008160009055506001016108af565b5090565b905600a165627a7a723058200ae0215fae320b646a22fdd58278b328f46d915bd65ddbfeb5b4a09643d6e0220029000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000000010000000000000000000000007ffbe3512782069be388f41be4d8eb350672d3a500000000000000000000000000000000000000000000000000000000000000e88b98f0e95488c3849ba24320a00a047c39b3ca3af8f066d82d849104eb0218150baf61620b321c8d2db0c8e351fd17826cd2cd13296287fb02cab742db410ba4346b4c13c1f81cc52dd5ee3dd7773e23842fe5d8e29db6da6f8f6f42964f118486b0c8d7971cfb8ed4926622774c92e495ae47dd481eb03090ad09a1fb4a2cd0ad4b108efeb408f160f34fae6f6d9843574f3e37dd11ec54dd69fa7dfa919257e760a78d6cccec92785381f9554b3da1249c4844e259b0f57180034fb63bd6136132f822f6472a31c68b8a60646bcbddfa4ddd3be0fe4457a4f691a6542bf798a4fa6b0990284166000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001056e81f171bcc55a6ff8345e692c0f86e000000000000000000000000000000006060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d833981016060604052341561000f57600080fd5b604051610b0d380380610b0d83398101 diff --git a/miner/src/pool/scoring.rs b/miner/src/pool/scoring.rs new file mode 100644 index 0000000000000000000000000000000000000000..e7551ed6a3dadfeb19f62b1aba7231443f7aaf4e --- /dev/null +++ b/miner/src/pool/scoring.rs @@ -0,0 +1,207 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Transaction Scoring and Ordering +//! +//! Ethereum transactions from the same sender are ordered by `nonce`. +//! Low nonces need to be included first. If there are two transactions from the same sender +//! and with the same `nonce` only one of them can be included. +//! We choose the one with higher gas price, but also require that gas price increment +//! is high enough to prevent attacking miners by requiring them to reshuffle/reexecute +//! the queue too often. +//! +//! Transactions between senders are prioritized using `gas price`. Higher `gas price` +//! yields more profits for miners. Additionally we prioritize transactions that originate +//! from our local node (own transactions). + +use std::cmp; + +use ethereum_types::U256; +use txpool; +use super::{PrioritizationStrategy, VerifiedTransaction}; + +/// Transaction with the same (sender, nonce) can be replaced only if +/// `new_gas_price > old_gas_price + old_gas_price >> SHIFT` +const GAS_PRICE_BUMP_SHIFT: usize = 3; // 2 = 25%, 3 = 12.5%, 4 = 6.25% + +/// Simple, gas-price based scoring for transactions. +/// +/// NOTE: Currently penalization does not apply to new transactions that enter the pool. +/// We might want to store penalization status in some persistent state. +#[derive(Debug)] +pub struct NonceAndGasPrice(pub PrioritizationStrategy); + +impl txpool::Scoring for NonceAndGasPrice { + type Score = U256; + type Event = (); + + fn compare(&self, old: &VerifiedTransaction, other: &VerifiedTransaction) -> cmp::Ordering { + old.transaction.nonce.cmp(&other.transaction.nonce) + } + + fn choose(&self, old: &VerifiedTransaction, new: &VerifiedTransaction) -> txpool::scoring::Choice { + if old.transaction.nonce != new.transaction.nonce { + return txpool::scoring::Choice::InsertNew + } + + let old_gp = old.transaction.gas_price; + let new_gp = new.transaction.gas_price; + + let min_required_gp = old_gp + (old_gp >> GAS_PRICE_BUMP_SHIFT); + + match min_required_gp.cmp(&new_gp) { + cmp::Ordering::Greater => txpool::scoring::Choice::RejectNew, + _ => txpool::scoring::Choice::ReplaceOld, + } + } + + fn update_scores(&self, txs: &[txpool::Transaction], scores: &mut [U256], change: txpool::scoring::Change) { + use self::txpool::scoring::Change; + + match change { + Change::Culled(_) => {}, + Change::RemovedAt(_) => {} + Change::InsertedAt(i) | Change::ReplacedAt(i) => { + assert!(i < txs.len()); + assert!(i < scores.len()); + + scores[i] = txs[i].transaction.transaction.gas_price; + let boost = match txs[i].priority() { + super::Priority::Local => 15, + super::Priority::Retracted => 10, + super::Priority::Regular => 0, + }; + scores[i] = scores[i] << boost; + }, + // We are only sending an event in case of penalization. + // So just lower the priority of all non-local transactions. + Change::Event(_) => { + for (score, tx) in scores.iter_mut().zip(txs) { + // Never penalize local transactions. + if !tx.priority().is_local() { + *score = *score >> 3; + } + } + }, + } + } + + fn should_replace(&self, old: &VerifiedTransaction, new: &VerifiedTransaction) -> bool { + if old.sender == new.sender { + // prefer earliest transaction + if new.transaction.nonce < old.transaction.nonce { + return true + } + } + + // Always kick out non-local transactions in favour of local ones. + if new.priority().is_local() && !old.priority().is_local() { + return true; + } + // And never kick out local transactions in favour of external ones. + if !new.priority().is_local() && old.priority.is_local() { + return false; + } + + self.choose(old, new) == txpool::scoring::Choice::ReplaceOld + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use std::sync::Arc; + use pool::tests::tx::{Tx, TxExt}; + use txpool::Scoring; + + #[test] + fn should_replace_non_local_transaction_with_local_one() { + // given + let scoring = NonceAndGasPrice(PrioritizationStrategy::GasPriceOnly); + let tx1 = { + let tx = Tx::default().signed().verified(); + txpool::Transaction { + insertion_id: 0, + transaction: Arc::new(tx), + } + }; + let tx2 = { + let mut tx = Tx::default().signed().verified(); + tx.priority = ::pool::Priority::Local; + txpool::Transaction { + insertion_id: 0, + transaction: Arc::new(tx), + } + }; + + assert!(scoring.should_replace(&tx1, &tx2)); + assert!(!scoring.should_replace(&tx2, &tx1)); + } + + #[test] + fn should_calculate_score_correctly() { + // given + let scoring = NonceAndGasPrice(PrioritizationStrategy::GasPriceOnly); + let (tx1, tx2, tx3) = Tx::default().signed_triple(); + let transactions = vec![tx1, tx2, tx3].into_iter().enumerate().map(|(i, tx)| { + let mut verified = tx.verified(); + verified.priority = match i { + 0 => ::pool::Priority::Local, + 1 => ::pool::Priority::Retracted, + _ => ::pool::Priority::Regular, + }; + txpool::Transaction { + insertion_id: 0, + transaction: Arc::new(verified), + } + }).collect::>(); + let initial_scores = vec![U256::from(0), 0.into(), 0.into()]; + + // No update required + let mut scores = initial_scores.clone(); + scoring.update_scores(&transactions, &mut *scores, txpool::scoring::Change::Culled(0)); + scoring.update_scores(&transactions, &mut *scores, txpool::scoring::Change::Culled(1)); + scoring.update_scores(&transactions, &mut *scores, txpool::scoring::Change::Culled(2)); + assert_eq!(scores, initial_scores); + let mut scores = initial_scores.clone(); + scoring.update_scores(&transactions, &mut *scores, txpool::scoring::Change::RemovedAt(0)); + scoring.update_scores(&transactions, &mut *scores, txpool::scoring::Change::RemovedAt(1)); + scoring.update_scores(&transactions, &mut *scores, txpool::scoring::Change::RemovedAt(2)); + assert_eq!(scores, initial_scores); + + // Compute score at given index + let mut scores = initial_scores.clone(); + scoring.update_scores(&transactions, &mut *scores, txpool::scoring::Change::InsertedAt(0)); + assert_eq!(scores, vec![32768.into(), 0.into(), 0.into()]); + scoring.update_scores(&transactions, &mut *scores, txpool::scoring::Change::InsertedAt(1)); + assert_eq!(scores, vec![32768.into(), 1024.into(), 0.into()]); + scoring.update_scores(&transactions, &mut *scores, txpool::scoring::Change::InsertedAt(2)); + assert_eq!(scores, vec![32768.into(), 1024.into(), 1.into()]); + + let mut scores = initial_scores.clone(); + scoring.update_scores(&transactions, &mut *scores, txpool::scoring::Change::ReplacedAt(0)); + assert_eq!(scores, vec![32768.into(), 0.into(), 0.into()]); + scoring.update_scores(&transactions, &mut *scores, txpool::scoring::Change::ReplacedAt(1)); + assert_eq!(scores, vec![32768.into(), 1024.into(), 0.into()]); + scoring.update_scores(&transactions, &mut *scores, txpool::scoring::Change::ReplacedAt(2)); + assert_eq!(scores, vec![32768.into(), 1024.into(), 1.into()]); + + // Check penalization + scoring.update_scores(&transactions, &mut *scores, txpool::scoring::Change::Event(())); + assert_eq!(scores, vec![32768.into(), 128.into(), 0.into()]); + } +} diff --git a/miner/src/pool/tests/client.rs b/miner/src/pool/tests/client.rs new file mode 100644 index 0000000000000000000000000000000000000000..08b43f12adf0f76a9b4a40b1187054c383b21a6d --- /dev/null +++ b/miner/src/pool/tests/client.rs @@ -0,0 +1,148 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use std::sync::{atomic, Arc}; + +use ethereum_types::{U256, H256, Address}; +use rlp::Rlp; +use transaction::{self, Transaction, SignedTransaction, UnverifiedTransaction}; + +use pool; +use pool::client::AccountDetails; + +const MAX_TRANSACTION_SIZE: usize = 15 * 1024; + +#[derive(Debug, Clone)] +pub struct TestClient { + verification_invoked: Arc, + account_details: AccountDetails, + gas_required: U256, + is_service_transaction: bool, + local_address: Address, + max_transaction_size: usize, +} + +impl Default for TestClient { + fn default() -> Self { + TestClient { + verification_invoked: Default::default(), + account_details: AccountDetails { + nonce: 123.into(), + balance: 63_100.into(), + is_local: false, + }, + gas_required: 21_000.into(), + is_service_transaction: false, + local_address: Default::default(), + max_transaction_size: MAX_TRANSACTION_SIZE, + } + } +} + +impl TestClient { + pub fn new() -> Self { + TestClient::default() + } + + pub fn with_balance>(mut self, balance: T) -> Self { + self.account_details.balance = balance.into(); + self + } + + pub fn with_nonce>(mut self, nonce: T) -> Self { + self.account_details.nonce = nonce.into(); + self + } + + pub fn with_gas_required>(mut self, gas_required: T) -> Self { + self.gas_required = gas_required.into(); + self + } + + pub fn with_local(mut self, address: &Address) -> Self { + self.local_address = *address; + self + } + + pub fn with_service_transaction(mut self) -> Self { + self.is_service_transaction = true; + self + } + + pub fn verify>(&self, tx: T) -> pool::VerifiedTransaction { + let tx = tx.into(); + pool::VerifiedTransaction { + hash: tx.hash(), + sender: tx.sender(), + priority: pool::Priority::Regular, + transaction: tx, + insertion_id: 1, + } + } + + pub fn was_verification_triggered(&self) -> bool { + self.verification_invoked.load(atomic::Ordering::SeqCst) + } +} + +impl pool::client::Client for TestClient { + fn transaction_already_included(&self, _hash: &H256) -> bool { + false + } + + fn verify_transaction(&self, tx: UnverifiedTransaction) + -> Result + { + self.verification_invoked.store(true, atomic::Ordering::SeqCst); + Ok(SignedTransaction::new(tx)?) + } + + fn account_details(&self, address: &Address) -> AccountDetails { + let mut details = self.account_details.clone(); + if address == &self.local_address { + details.is_local = true; + } + + details + } + + fn required_gas(&self, _tx: &Transaction) -> U256 { + self.gas_required + } + + fn transaction_type(&self, _tx: &SignedTransaction) -> pool::client::TransactionType { + if self.is_service_transaction { + pool::client::TransactionType::Service + } else { + pool::client::TransactionType::Regular + } + } + + fn decode_transaction(&self, transaction: &[u8]) -> Result { + let rlp = Rlp::new(&transaction); + if rlp.as_raw().len() > self.max_transaction_size { + return Err(transaction::Error::TooBig) + } + rlp.as_val().map_err(|e| transaction::Error::InvalidRlp(e.to_string())) + } + +} + +impl pool::client::NonceClient for TestClient { + fn account_nonce(&self, _address: &Address) -> U256 { + self.account_details.nonce + } +} diff --git a/miner/src/pool/tests/mod.rs b/miner/src/pool/tests/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..ef83db4a90137b3e52f9a63e3ce2f50064b61117 --- /dev/null +++ b/miner/src/pool/tests/mod.rs @@ -0,0 +1,894 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use ethereum_types::U256; +use transaction::{self, PendingTransaction}; +use txpool; + +use pool::{verifier, TransactionQueue, PrioritizationStrategy, PendingSettings, PendingOrdering}; + +pub mod tx; +pub mod client; + +use self::tx::{Tx, TxExt, PairExt}; +use self::client::TestClient; + +fn new_queue() -> TransactionQueue { + TransactionQueue::new( + txpool::Options { + max_count: 3, + max_per_sender: 3, + max_mem_usage: 50 + }, + verifier::Options { + minimal_gas_price: 1.into(), + block_gas_limit: 1_000_000.into(), + tx_gas_limit: 1_000_000.into(), + }, + PrioritizationStrategy::GasPriceOnly, + ) +} + +#[test] +fn should_return_correct_nonces_when_dropped_because_of_limit() { + // given + let txq = TransactionQueue::new( + txpool::Options { + max_count: 3, + max_per_sender: 1, + max_mem_usage: 50 + }, + verifier::Options { + minimal_gas_price: 1.into(), + block_gas_limit: 1_000_000.into(), + tx_gas_limit: 1_000_000.into(), + }, + PrioritizationStrategy::GasPriceOnly, + ); + let (tx1, tx2) = Tx::gas_price(2).signed_pair(); + let sender = tx1.sender(); + let nonce = tx1.nonce; + + // when + let r1= txq.import(TestClient::new(), vec![tx1].local()); + let r2= txq.import(TestClient::new(), vec![tx2].local()); + assert_eq!(r1, vec![Ok(())]); + assert_eq!(r2, vec![Err(transaction::Error::LimitReached)]); + assert_eq!(txq.status().status.transaction_count, 1); + + // then + assert_eq!(txq.next_nonce(TestClient::new(), &sender), Some(nonce + 1.into())); + + // when + let tx1 = Tx::gas_price(2).signed(); + let tx2 = Tx::gas_price(2).signed(); + let tx3 = Tx::gas_price(1).signed(); + let tx4 = Tx::gas_price(3).signed(); + let res = txq.import(TestClient::new(), vec![tx1, tx2].local()); + let res2 = txq.import(TestClient::new(), vec![tx3, tx4].local()); + + // then + assert_eq!(res, vec![Ok(()), Ok(())]); + assert_eq!(res2, vec![Err(transaction::Error::LimitReached), Ok(())]); + assert_eq!(txq.status().status.transaction_count, 3); + // First inserted transacton got dropped because of limit + assert_eq!(txq.next_nonce(TestClient::new(), &sender), None); +} + +#[test] +fn should_handle_same_transaction_imported_twice_with_different_state_nonces() { + // given + let txq = new_queue(); + let (tx, tx2) = Tx::default().signed_replacement(); + let hash = tx2.hash(); + let client = TestClient::new().with_nonce(122); + + // First insert one transaction to future + let res = txq.import(client.clone(), vec![tx].local()); + assert_eq!(res, vec![Ok(())]); + // next_nonce === None -> transaction is in future + assert_eq!(txq.next_nonce(client.clone(), &tx2.sender()), None); + + // now import second transaction to current + let res = txq.import(TestClient::new(), vec![tx2.local()]); + + // and then there should be only one transaction in current (the one with higher gas_price) + assert_eq!(res, vec![Ok(())]); + assert_eq!(txq.status().status.transaction_count, 1); + let top = txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)); + assert_eq!(top[0].hash, hash); +} + +#[test] +fn should_move_all_transactions_from_future() { + // given + let txq = new_queue(); + let txs = Tx::default().signed_pair(); + let (hash, hash2) = txs.hash(); + let (tx, tx2) = txs; + let client = TestClient::new().with_nonce(122); + + // First insert one transaction to future + let res = txq.import(client.clone(), vec![tx.local()]); + assert_eq!(res, vec![Ok(())]); + // next_nonce === None -> transaction is in future + assert_eq!(txq.next_nonce(client.clone(), &tx2.sender()), None); + + // now import second transaction to current + let res = txq.import(client.clone(), vec![tx2.local()]); + + // then + assert_eq!(res, vec![Ok(())]); + assert_eq!(txq.status().status.transaction_count, 2); + let top = txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)); + assert_eq!(top[0].hash, hash); + assert_eq!(top[1].hash, hash2); +} + +#[test] +fn should_drop_transactions_from_senders_without_balance() { + // given + let txq = new_queue(); + let tx = Tx::default().signed(); + let client = TestClient::new().with_balance(1); + + // when + let res = txq.import(client, vec![tx.local()]); + + // then + assert_eq!(res, vec![Err(transaction::Error::InsufficientBalance { + balance: U256::from(1), + cost: U256::from(21_100), + })]); + assert_eq!(txq.status().status.transaction_count, 0); +} + +#[test] +fn should_not_import_transaction_below_min_gas_price_threshold_if_external() { + // given + let txq = new_queue(); + let tx = Tx::default(); + txq.set_verifier_options(verifier::Options { + minimal_gas_price: 3.into(), + ..Default::default() + }); + + // when + let res = txq.import(TestClient::new(), vec![tx.signed().unverified()]); + + // then + assert_eq!(res, vec![Err(transaction::Error::InsufficientGasPrice { + minimal: U256::from(3), + got: U256::from(1), + })]); + assert_eq!(txq.status().status.transaction_count, 0); +} + +#[test] +fn should_import_transaction_below_min_gas_price_threshold_if_local() { + // given + let txq = new_queue(); + let tx = Tx::default(); + txq.set_verifier_options(verifier::Options { + minimal_gas_price: 3.into(), + ..Default::default() + }); + + // when + let res = txq.import(TestClient::new(), vec![tx.signed().local()]); + + // then + assert_eq!(res, vec![Ok(())]); + assert_eq!(txq.status().status.transaction_count, 1); +} + +#[test] +fn should_import_txs_from_same_sender() { + // given + let txq = new_queue(); + + let txs = Tx::default().signed_pair(); + let (hash, hash2) = txs.hash(); + + // when + txq.import(TestClient::new(), txs.local().into_vec()); + + // then + let top = txq.pending(TestClient::new(), PendingSettings::all_prioritized(0 ,0)); + assert_eq!(top[0].hash, hash); + assert_eq!(top[1].hash, hash2); + assert_eq!(top.len(), 2); +} + +#[test] +fn should_prioritize_local_transactions_within_same_nonce_height() { + // given + let txq = new_queue(); + let tx = Tx::default().signed(); + // the second one has same nonce but higher `gas_price` + let tx2 = Tx::gas_price(2).signed(); + let (hash, hash2) = (tx.hash(), tx2.hash()); + let client = TestClient::new().with_local(&tx.sender()); + + // when + // first insert the one with higher gas price + let res = txq.import(client.clone(), vec![tx.local(), tx2.unverified()]); + assert_eq!(res, vec![Ok(()), Ok(())]); + + // then + let top = txq.pending(client, PendingSettings::all_prioritized(0, 0)); + assert_eq!(top[0].hash, hash); // local should be first + assert_eq!(top[1].hash, hash2); + assert_eq!(top.len(), 2); +} + +#[test] +fn should_prioritize_reimported_transactions_within_same_nonce_height() { + // given + let txq = new_queue(); + let tx = Tx::default().signed(); + // the second one has same nonce but higher `gas_price` + let tx2 = Tx::gas_price(2).signed(); + let (hash, hash2) = (tx.hash(), tx2.hash()); + + // when + // first insert local one with higher gas price + // then the one with lower gas price, but from retracted block + let res = txq.import(TestClient::new(), vec![tx2.unverified(), tx.retracted()]); + assert_eq!(res, vec![Ok(()), Ok(())]); + + // then + let top = txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)); + assert_eq!(top[0].hash, hash); // retracted should be first + assert_eq!(top[1].hash, hash2); + assert_eq!(top.len(), 2); +} + +#[test] +fn should_not_prioritize_local_transactions_with_different_nonce_height() { + // given + let txq = new_queue(); + let txs = Tx::default().signed_pair(); + let (hash, hash2) = txs.hash(); + let (tx, tx2) = txs; + + // when + let res = txq.import(TestClient::new(), vec![tx.unverified(), tx2.local()]); + assert_eq!(res, vec![Ok(()), Ok(())]); + + // then + let top = txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)); + assert_eq!(top[0].hash, hash); + assert_eq!(top[1].hash, hash2); + assert_eq!(top.len(), 2); +} + +#[test] +fn should_put_transaction_to_futures_if_gap_detected() { + // given + let txq = new_queue(); + let (tx, _, tx2) = Tx::default().signed_triple(); + let hash = tx.hash(); + + // when + let res = txq.import(TestClient::new(), vec![tx, tx2].local()); + + // then + assert_eq!(res, vec![Ok(()), Ok(())]); + let top = txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)); + assert_eq!(top.len(), 1); + assert_eq!(top[0].hash, hash); +} + +#[test] +fn should_handle_min_block() { + // given + let txq = new_queue(); + + let (tx, tx2) = Tx::default().signed_pair(); + + // when + let res = txq.import(TestClient::new(), vec![ + verifier::Transaction::Local(PendingTransaction::new(tx, transaction::Condition::Number(1).into())), + tx2.local() + ]); + assert_eq!(res, vec![Ok(()), Ok(())]); + + // then + let top = txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)); + assert_eq!(top.len(), 0); + let top = txq.pending(TestClient::new(), PendingSettings::all_prioritized(1, 0)); + assert_eq!(top.len(), 2); +} + +#[test] +fn should_correctly_update_futures_when_removing() { + // given + let txq = new_queue(); + let txs= Tx::default().signed_pair(); + + let res = txq.import(TestClient::new().with_nonce(121), txs.local().into_vec()); + assert_eq!(res, vec![Ok(()), Ok(())]); + assert_eq!(txq.status().status.transaction_count, 2); + + // when + txq.cull(TestClient::new().with_nonce(125)); + // should remove both transactions since they are stalled + + // then + assert_eq!(txq.status().status.transaction_count, 0); +} + +#[test] +fn should_move_transactions_if_gap_filled() { + // given + let txq = new_queue(); + let (tx, tx1, tx2) = Tx::default().signed_triple(); + + let res = txq.import(TestClient::new(), vec![tx, tx2].local()); + assert_eq!(res, vec![Ok(()), Ok(())]); + assert_eq!(txq.status().status.transaction_count, 2); + assert_eq!(txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)).len(), 1); + + // when + let res = txq.import(TestClient::new(), vec![tx1.local()]); + assert_eq!(res, vec![Ok(())]); + + // then + assert_eq!(txq.status().status.transaction_count, 3); + assert_eq!(txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)).len(), 3); +} + +#[test] +fn should_remove_transaction() { + // given + let txq = new_queue(); + let (tx, _, tx2) = Tx::default().signed_triple(); + + let res = txq.import(TestClient::default(), vec![tx, tx2].local()); + assert_eq!(res, vec![Ok(()), Ok(())]); + assert_eq!(txq.status().status.transaction_count, 2); + assert_eq!(txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)).len(), 1); + + // when + txq.cull(TestClient::new().with_nonce(124)); + assert_eq!(txq.status().status.transaction_count, 1); + assert_eq!(txq.pending(TestClient::new().with_nonce(125), PendingSettings::all_prioritized(0, 0)).len(), 1); + txq.cull(TestClient::new().with_nonce(126)); + + // then + assert_eq!(txq.status().status.transaction_count, 0); +} + +#[test] +fn should_move_transactions_to_future_if_gap_introduced() { + // given + let txq = new_queue(); + let (tx, tx2) = Tx::default().signed_pair(); + let hash = tx.hash(); + let tx3 = Tx::default().signed(); + + let res = txq.import(TestClient::new(), vec![tx3, tx2].local()); + assert_eq!(res, vec![Ok(()), Ok(())]); + assert_eq!(txq.status().status.transaction_count, 2); + assert_eq!(txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)).len(), 1); + + let res = txq.import(TestClient::new(), vec![tx].local()); + assert_eq!(res, vec![Ok(())]); + assert_eq!(txq.status().status.transaction_count, 3); + assert_eq!(txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)).len(), 3); + + // when + txq.remove(vec![&hash], true); + + // then + assert_eq!(txq.status().status.transaction_count, 2); + assert_eq!(txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)).len(), 1); +} + +#[test] +fn should_clear_queue() { + // given + let txq = new_queue(); + let txs = Tx::default().signed_pair(); + + // add + txq.import(TestClient::new(), txs.local().into_vec()); + assert_eq!(txq.status().status.transaction_count, 2); + + // when + txq.clear(); + + // then + assert_eq!(txq.status().status.transaction_count, 0); +} + +#[test] +fn should_prefer_current_transactions_when_hitting_the_limit() { + // given + let txq = TransactionQueue::new( + txpool::Options { + max_count: 1, + max_per_sender: 2, + max_mem_usage: 50 + }, + verifier::Options { + minimal_gas_price: 1.into(), + block_gas_limit: 1_000_000.into(), + tx_gas_limit: 1_000_000.into(), + }, + PrioritizationStrategy::GasPriceOnly, + ); + let (tx, tx2) = Tx::default().signed_pair(); + let hash = tx.hash(); + let sender = tx.sender(); + + let res = txq.import(TestClient::new(), vec![tx2.unverified()]); + assert_eq!(res, vec![Ok(())]); + assert_eq!(txq.status().status.transaction_count, 1); + + // when + let res = txq.import(TestClient::new(), vec![tx.unverified()]); + + // then + assert_eq!(res, vec![Ok(())]); + assert_eq!(txq.status().status.transaction_count, 1); + + let top = txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)); + assert_eq!(top.len(), 1); + assert_eq!(top[0].hash, hash); + assert_eq!(txq.next_nonce(TestClient::new(), &sender), Some(124.into())); +} + +#[test] +fn should_drop_transactions_with_old_nonces() { + let txq = new_queue(); + let tx = Tx::default().signed(); + + // when + let res = txq.import(TestClient::new().with_nonce(125), vec![tx.unverified()]); + + // then + assert_eq!(res, vec![Err(transaction::Error::Old)]); + assert_eq!(txq.status().status.transaction_count, 0); +} + +#[test] +fn should_not_insert_same_transaction_twice() { + // given + let txq = new_queue(); + let (_tx1, tx2) = Tx::default().signed_pair(); + let res = txq.import(TestClient::new(), vec![tx2.clone().local()]); + assert_eq!(res, vec![Ok(())]); + assert_eq!(txq.status().status.transaction_count, 1); + + // when + let res = txq.import(TestClient::new(), vec![tx2.local()]); + + // then + assert_eq!(res, vec![Err(transaction::Error::AlreadyImported)]); + assert_eq!(txq.status().status.transaction_count, 1); +} + +#[test] +fn should_accept_same_transaction_twice_if_removed() { + // given + let txq = new_queue(); + let txs = Tx::default().signed_pair(); + let (tx1, _) = txs.clone(); + let (hash, _) = txs.hash(); + + let res = txq.import(TestClient::new(), txs.local().into_vec()); + assert_eq!(res, vec![Ok(()), Ok(())]); + assert_eq!(txq.status().status.transaction_count, 2); + assert_eq!(txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)).len(), 2); + + // when + txq.remove(vec![&hash], true); + assert_eq!(txq.status().status.transaction_count, 1); + assert_eq!(txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)).len(), 0); + + let res = txq.import(TestClient::new(), vec![tx1].local()); + assert_eq!(res, vec![Ok(())]); + + // then + assert_eq!(txq.status().status.transaction_count, 2); + assert_eq!(txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)).len(), 2); +} + +#[test] +fn should_not_replace_same_transaction_if_the_fee_is_less_than_minimal_bump() { + // given + let txq = new_queue(); + let (tx, tx2) = Tx::gas_price(20).signed_replacement(); + let (tx3, tx4) = Tx::gas_price(1).signed_replacement(); + let client = TestClient::new().with_balance(1_000_000); + + // when + let res = txq.import(client.clone(), vec![tx, tx3].local()); + assert_eq!(res, vec![Ok(()), Ok(())]); + + let res = txq.import(client.clone(), vec![tx2, tx4].local()); + + // then + assert_eq!(res, vec![Err(transaction::Error::TooCheapToReplace), Ok(())]); + assert_eq!(txq.status().status.transaction_count, 2); + assert_eq!(txq.pending(client.clone(), PendingSettings::all_prioritized(0, 0))[0].signed().gas_price, U256::from(20)); + assert_eq!(txq.pending(client.clone(), PendingSettings::all_prioritized(0, 0))[1].signed().gas_price, U256::from(2)); +} + +#[test] +fn should_return_none_when_transaction_from_given_address_does_not_exist() { + // given + let txq = new_queue(); + + // then + assert_eq!(txq.next_nonce(TestClient::new(), &Default::default()), None); +} + +#[test] +fn should_return_correct_nonce_when_transactions_from_given_address_exist() { + // given + let txq = new_queue(); + let tx = Tx::default().signed(); + let from = tx.sender(); + let nonce = tx.nonce; + + // when + txq.import(TestClient::new(), vec![tx.local()]); + + // then + assert_eq!(txq.next_nonce(TestClient::new(), &from), Some(nonce + 1.into())); +} + +#[test] +fn should_return_valid_last_nonce_after_cull() { + // given + let txq = new_queue(); + let (tx1, _, tx2) = Tx::default().signed_triple(); + let sender = tx1.sender(); + + // when + // Second should go to future + let res = txq.import(TestClient::new(), vec![tx1, tx2].local()); + assert_eq!(res, vec![Ok(()), Ok(())]); + // Now block is imported + let client = TestClient::new().with_nonce(124); + txq.cull(client.clone()); + // tx2 should be not be promoted to current + assert_eq!(txq.pending(client.clone(), PendingSettings::all_prioritized(0, 0)).len(), 0); + + // then + assert_eq!(txq.next_nonce(client.clone(), &sender), None); + assert_eq!(txq.next_nonce(client.with_nonce(125), &sender), Some(126.into())); +} + +#[test] +fn should_return_true_if_there_is_local_transaction_pending() { + // given + let txq = new_queue(); + let (tx1, tx2) = Tx::default().signed_pair(); + assert_eq!(txq.has_local_pending_transactions(), false); + let client = TestClient::new().with_local(&tx1.sender()); + + // when + let res = txq.import(client.clone(), vec![tx1.unverified(), tx2.local()]); + assert_eq!(res, vec![Ok(()), Ok(())]); + + // then + assert_eq!(txq.has_local_pending_transactions(), true); +} + +#[test] +fn should_reject_transactions_below_base_gas() { + // given + let txq = new_queue(); + let tx = Tx::default().signed(); + + // when + let res = txq.import(TestClient::new().with_gas_required(100_001), vec![tx].local()); + + // then + assert_eq!(res, vec![Err(transaction::Error::InsufficientGas { + minimal: 100_001.into(), + got: 21_000.into(), + })]); +} + +#[test] +fn should_remove_out_of_date_transactions_occupying_queue() { + // given + let txq = TransactionQueue::new( + txpool::Options { + max_count: 105, + max_per_sender: 3, + max_mem_usage: 5_000_000, + }, + verifier::Options { + minimal_gas_price: 10.into(), + ..Default::default() + }, + PrioritizationStrategy::GasPriceOnly, + ); + // that transaction will be occupying the queue + let (_, tx) = Tx::default().signed_pair(); + let res = txq.import(TestClient::new(), vec![tx.local()]); + assert_eq!(res, vec![Ok(())]); + // This should not clear the transaction (yet) + txq.cull(TestClient::new()); + assert_eq!(txq.status().status.transaction_count, 1); + + // Now insert at least 100 transactions to have the other one marked as future. + for _ in 0..34 { + let (tx1, tx2, tx3) = Tx::default().signed_triple(); + txq.import(TestClient::new(), vec![tx1, tx2, tx3].local()); + } + assert_eq!(txq.status().status.transaction_count, 103); + + // when + txq.cull(TestClient::new()); + + // then + assert_eq!(txq.status().status.transaction_count, 102); +} + +#[test] +fn should_accept_local_transactions_below_min_gas_price() { + // given + let txq = TransactionQueue::new( + txpool::Options { + max_count: 3, + max_per_sender: 3, + max_mem_usage: 50 + }, + verifier::Options { + minimal_gas_price: 10.into(), + ..Default::default() + }, + PrioritizationStrategy::GasPriceOnly, + ); + let tx = Tx::gas_price(1).signed(); + + // when + let res = txq.import(TestClient::new(), vec![tx.local()]); + assert_eq!(res, vec![Ok(())]); + + // then + assert_eq!(txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)).len(), 1); +} + +#[test] +fn should_accept_local_service_transaction() { + // given + let txq = new_queue(); + let tx = Tx::gas_price(0).signed(); + + // when + let res = txq.import( + TestClient::new() + .with_local(&tx.sender()), + vec![tx.local()] + ); + assert_eq!(res, vec![Ok(())]); + + // then + assert_eq!(txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)).len(), 1); +} + +#[test] +fn should_not_accept_external_service_transaction_if_sender_not_certified() { + // given + let txq = new_queue(); + let tx1 = Tx::gas_price(0).signed().unverified(); + let tx2 = Tx::gas_price(0).signed().retracted(); + let tx3 = Tx::gas_price(0).signed().unverified(); + + // when + let res = txq.import(TestClient::new(), vec![tx1, tx2]); + assert_eq!(res, vec![ + Err(transaction::Error::InsufficientGasPrice { + minimal: 1.into(), + got: 0.into(), + }), + Err(transaction::Error::InsufficientGasPrice { + minimal: 1.into(), + got: 0.into(), + }), + ]); + + // then + let res = txq.import(TestClient::new().with_service_transaction(), vec![tx3]); + assert_eq!(res, vec![Ok(())]); +} + +#[test] +fn should_not_return_transactions_over_nonce_cap() { + // given + let txq = new_queue(); + let (tx1, tx2, tx3) = Tx::default().signed_triple(); + let res = txq.import( + TestClient::new(), + vec![tx1, tx2, tx3].local() + ); + assert_eq!(res, vec![Ok(()), Ok(()), Ok(())]); + + // when + let all = txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)); + // This should invalidate the cache! + let limited = txq.pending(TestClient::new(), PendingSettings { + block_number: 0, + current_timestamp: 0, + nonce_cap: Some(123.into()), + max_len: usize::max_value(), + ordering: PendingOrdering::Priority, + }); + + // then + assert_eq!(all.len(), 3); + assert_eq!(limited.len(), 1); +} + +#[test] +fn should_return_cached_pending_even_if_unordered_is_requested() { + // given + let txq = new_queue(); + let tx1 = Tx::default().signed(); + let (tx2_1, tx2_2)= Tx::default().signed_pair(); + let tx2_1_hash = tx2_1.hash(); + let res = txq.import(TestClient::new(), vec![tx1].unverified()); + assert_eq!(res, vec![Ok(())]); + let res = txq.import(TestClient::new(), vec![tx2_1, tx2_2].local()); + assert_eq!(res, vec![Ok(()), Ok(())]); + + // when + let all = txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)); + assert_eq!(all[0].hash, tx2_1_hash); + assert_eq!(all.len(), 3); + + // This should not invalidate the cache! + let limited = txq.pending(TestClient::new(), PendingSettings { + block_number: 0, + current_timestamp: 0, + nonce_cap: None, + max_len: 3, + ordering: PendingOrdering::Unordered, + }); + + // then + assert_eq!(all, limited); +} + +#[test] +fn should_return_unordered_and_not_populate_the_cache() { + // given + let txq = new_queue(); + let tx1 = Tx::default().signed(); + let (tx2_1, tx2_2)= Tx::default().signed_pair(); + let res = txq.import(TestClient::new(), vec![tx1].unverified()); + assert_eq!(res, vec![Ok(())]); + let res = txq.import(TestClient::new(), vec![tx2_1, tx2_2].local()); + assert_eq!(res, vec![Ok(()), Ok(())]); + + // when + // This should not invalidate the cache! + let limited = txq.pending(TestClient::new(), PendingSettings { + block_number: 0, + current_timestamp: 0, + nonce_cap: None, + max_len: usize::max_value(), + ordering: PendingOrdering::Unordered, + }); + + // then + assert_eq!(limited.len(), 3); + assert!(!txq.is_pending_cached()); +} + +#[test] +fn should_clear_cache_after_timeout_for_local() { + // given + let txq = new_queue(); + let (tx, tx2) = Tx::default().signed_pair(); + let res = txq.import(TestClient::new(), vec![ + verifier::Transaction::Local(PendingTransaction::new(tx, transaction::Condition::Timestamp(1000).into())), + tx2.local() + ]); + assert_eq!(res, vec![Ok(()), Ok(())]); + + // This should populate cache and set timestamp to 1 + // when + assert_eq!(txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 1)).len(), 0); + assert_eq!(txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 1000)).len(), 0); + + // This should invalidate the cache and trigger transaction ready. + // then + assert_eq!(txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 1002)).len(), 2); +} + +#[test] +fn should_reject_big_transaction() { + let txq = new_queue(); + let big_tx = Tx::default().big_one(); + let res = txq.import(TestClient::new(), vec![ + verifier::Transaction::Local(PendingTransaction::new(big_tx, transaction::Condition::Timestamp(1000).into())) + ]); + assert_eq!(res, vec![Err(transaction::Error::TooBig)]); +} + +#[test] +fn should_include_local_transaction_to_a_full_pool() { + // given + let txq = TransactionQueue::new( + txpool::Options { + max_count: 1, + max_per_sender: 2, + max_mem_usage: 50 + }, + verifier::Options { + minimal_gas_price: 1.into(), + block_gas_limit: 1_000_000.into(), + tx_gas_limit: 1_000_000.into(), + }, + PrioritizationStrategy::GasPriceOnly, + ); + let tx1 = Tx::gas_price(10_000).signed().unverified(); + let tx2 = Tx::gas_price(1).signed().local(); + + let res = txq.import(TestClient::new().with_balance(1_000_000_000), vec![tx1]); + assert_eq!(res, vec![Ok(())]); + assert_eq!(txq.status().status.transaction_count, 1); + + // when + let res = txq.import(TestClient::new(), vec![tx2]); + assert_eq!(res, vec![Ok(())]); + + // then + assert_eq!(txq.status().status.transaction_count, 1); +} + +#[test] +fn should_avoid_verifying_transaction_already_in_pool() { + // given + let txq = TransactionQueue::new( + txpool::Options { + max_count: 1, + max_per_sender: 2, + max_mem_usage: 50 + }, + verifier::Options { + minimal_gas_price: 1.into(), + block_gas_limit: 1_000_000.into(), + tx_gas_limit: 1_000_000.into(), + }, + PrioritizationStrategy::GasPriceOnly, + ); + let client = TestClient::new(); + let tx1 = Tx::default().signed().unverified(); + + let res = txq.import(client.clone(), vec![tx1.clone()]); + assert_eq!(res, vec![Ok(())]); + assert_eq!(txq.status().status.transaction_count, 1); + assert!(client.was_verification_triggered()); + + // when + let client = TestClient::new(); + let res = txq.import(client.clone(), vec![tx1]); + assert_eq!(res, vec![Err(transaction::Error::AlreadyImported)]); + assert!(!client.was_verification_triggered()); + + // then + assert_eq!(txq.status().status.transaction_count, 1); +} diff --git a/miner/src/pool/tests/tx.rs b/miner/src/pool/tests/tx.rs new file mode 100644 index 0000000000000000000000000000000000000000..a8b06f543668025e0eba726f5c81889086103ab0 --- /dev/null +++ b/miner/src/pool/tests/tx.rs @@ -0,0 +1,197 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use ethereum_types::{U256, H256}; +use ethkey::{Random, Generator}; +use rustc_hex::FromHex; +use transaction::{self, Transaction, SignedTransaction, UnverifiedTransaction}; + +use pool::{verifier, VerifiedTransaction}; + +#[derive(Clone)] +pub struct Tx { + nonce: u64, + gas: u64, + gas_price: u64, +} + +impl Default for Tx { + fn default() -> Self { + Tx { + nonce: 123, + gas: 21_000, + gas_price: 1, + } + } +} + +impl Tx { + pub fn gas_price(gas_price: u64) -> Self { + Tx { + gas_price, + ..Default::default() + } + } + + pub fn signed(self) -> SignedTransaction { + let keypair = Random.generate().unwrap(); + self.unsigned().sign(keypair.secret(), None) + } + + pub fn signed_pair(self) -> (SignedTransaction, SignedTransaction) { + let (tx1, tx2, _) = self.signed_triple(); + (tx1, tx2) + } + + pub fn signed_triple(mut self) -> (SignedTransaction, SignedTransaction, SignedTransaction) { + let keypair = Random.generate().unwrap(); + let tx1 = self.clone().unsigned().sign(keypair.secret(), None); + self.nonce += 1; + let tx2 = self.clone().unsigned().sign(keypair.secret(), None); + self.nonce += 1; + let tx3 = self.unsigned().sign(keypair.secret(), None); + + (tx1, tx2, tx3) + } + + pub fn signed_replacement(mut self) -> (SignedTransaction, SignedTransaction) { + let keypair = Random.generate().unwrap(); + let tx1 = self.clone().unsigned().sign(keypair.secret(), None); + self.gas_price += 1; + let tx2 = self.unsigned().sign(keypair.secret(), None); + + (tx1, tx2) + } + + pub fn unsigned(self) -> Transaction { + Transaction { + action: transaction::Action::Create, + value: U256::from(100), + data: "3331600055".from_hex().unwrap(), + gas: self.gas.into(), + gas_price: self.gas_price.into(), + nonce: self.nonce.into() + } + } + + pub fn big_one(self) -> SignedTransaction { + let keypair = Random.generate().unwrap(); + let tx = Transaction { + action: transaction::Action::Create, + value: U256::from(100), + data: include_str!("../res/big_transaction.data").from_hex().unwrap(), + gas: self.gas.into(), + gas_price: self.gas_price.into(), + nonce: self.nonce.into() + }; + tx.sign(keypair.secret(), None) + } +} +pub trait TxExt: Sized { + type Out; + type Verified; + type Hash; + + fn hash(&self) -> Self::Hash; + + fn local(self) -> Self::Out; + + fn retracted(self) -> Self::Out; + + fn unverified(self) -> Self::Out; + + fn verified(self) -> Self::Verified; +} + +impl TxExt for (A, B) where + A: TxExt, + B: TxExt, +{ + type Out = (O, O); + type Verified = (V, V); + type Hash = (H, H); + + fn hash(&self) -> Self::Hash { (self.0.hash(), self.1.hash()) } + fn local(self) -> Self::Out { (self.0.local(), self.1.local()) } + fn retracted(self) -> Self::Out { (self.0.retracted(), self.1.retracted()) } + fn unverified(self) -> Self::Out { (self.0.unverified(), self.1.unverified()) } + fn verified(self) -> Self::Verified { (self.0.verified(), self.1.verified()) } +} + +impl TxExt for SignedTransaction { + type Out = verifier::Transaction; + type Verified = VerifiedTransaction; + type Hash = H256; + + fn hash(&self) -> Self::Hash { + UnverifiedTransaction::hash(self) + } + + fn local(self) -> Self::Out { + verifier::Transaction::Local(self.into()) + } + + fn retracted(self) -> Self::Out { + verifier::Transaction::Retracted(self.into()) + } + + fn unverified(self) -> Self::Out { + verifier::Transaction::Unverified(self.into()) + } + + fn verified(self) -> Self::Verified { + VerifiedTransaction::from_pending_block_transaction(self) + } +} + +impl TxExt for Vec { + type Out = Vec; + type Verified = Vec; + type Hash = Vec; + + fn hash(&self) -> Self::Hash { + self.iter().map(|tx| tx.hash()).collect() + } + + fn local(self) -> Self::Out { + self.into_iter().map(Into::into).map(verifier::Transaction::Local).collect() + } + + fn retracted(self) -> Self::Out { + self.into_iter().map(Into::into).map(verifier::Transaction::Retracted).collect() + } + + fn unverified(self) -> Self::Out { + self.into_iter().map(Into::into).map(verifier::Transaction::Unverified).collect() + } + + fn verified(self) -> Self::Verified { + self.into_iter().map(VerifiedTransaction::from_pending_block_transaction).collect() + } +} + +pub trait PairExt { + type Type; + + fn into_vec(self) -> Vec; +} + +impl PairExt for (A, A) { + type Type = A; + fn into_vec(self) -> Vec { + vec![self.0, self.1] + } +} diff --git a/miner/src/pool/verifier.rs b/miner/src/pool/verifier.rs new file mode 100644 index 0000000000000000000000000000000000000000..5e6e22077a327c0c7c246a6f473f635a62278034 --- /dev/null +++ b/miner/src/pool/verifier.rs @@ -0,0 +1,296 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Transaction Verifier +//! +//! Responsible for verifying a transaction before importing to the pool. +//! Should make sure that the transaction is structuraly valid. +//! +//! May have some overlap with `Readiness` since we don't want to keep around +//! stalled transactions. + +use std::cmp; +use std::sync::Arc; +use std::sync::atomic::{self, AtomicUsize}; + +use ethereum_types::{U256, H256}; +use rlp::Encodable; +use transaction; +use txpool; + +use super::client::{Client, TransactionType}; +use super::VerifiedTransaction; + +/// Verification options. +#[derive(Debug, Clone, PartialEq)] +pub struct Options { + /// Minimal allowed gas price. + pub minimal_gas_price: U256, + /// Current block gas limit. + pub block_gas_limit: U256, + /// Maximal gas limit for a single transaction. + pub tx_gas_limit: U256, +} + +#[cfg(test)] +impl Default for Options { + fn default() -> Self { + Options { + minimal_gas_price: 0.into(), + block_gas_limit: U256::max_value(), + tx_gas_limit: U256::max_value(), + } + } +} + +/// Transaction to verify. +#[cfg_attr(test, derive(Clone))] +pub enum Transaction { + /// Fresh, never verified transaction. + /// + /// We need to do full verification of such transactions + Unverified(transaction::UnverifiedTransaction), + + /// Transaction from retracted block. + /// + /// We could skip some parts of verification of such transactions + Retracted(transaction::UnverifiedTransaction), + + /// Locally signed or retracted transaction. + /// + /// We can skip consistency verifications and just verify readiness. + Local(transaction::PendingTransaction), +} + +impl Transaction { + /// Return transaction hash + pub fn hash(&self) -> H256 { + match *self { + Transaction::Unverified(ref tx) => tx.hash(), + Transaction::Retracted(ref tx) => tx.hash(), + Transaction::Local(ref tx) => tx.hash(), + } + } + + fn gas(&self) -> &U256 { + match *self { + Transaction::Unverified(ref tx) => &tx.gas, + Transaction::Retracted(ref tx) => &tx.gas, + Transaction::Local(ref tx) => &tx.gas, + } + } + + fn gas_price(&self) -> &U256 { + match *self { + Transaction::Unverified(ref tx) => &tx.gas_price, + Transaction::Retracted(ref tx) => &tx.gas_price, + Transaction::Local(ref tx) => &tx.gas_price, + } + } + + fn transaction(&self) -> &transaction::Transaction { + match *self { + Transaction::Unverified(ref tx) => &*tx, + Transaction::Retracted(ref tx) => &*tx, + Transaction::Local(ref tx) => &*tx, + } + } + + fn is_local(&self) -> bool { + match *self { + Transaction::Local(..) => true, + _ => false, + } + } + + fn is_retracted(&self) -> bool { + match *self { + Transaction::Retracted(..) => true, + _ => false, + } + } +} + +/// Transaction verifier. +/// +/// Verification can be run in parallel for all incoming transactions. +#[derive(Debug)] +pub struct Verifier { + client: C, + options: Options, + id: Arc, +} + +impl Verifier { + /// Creates new transaction verfier with specified options. + pub fn new(client: C, options: Options, id: Arc) -> Self { + Verifier { + client, + options, + id, + } + } +} + +impl txpool::Verifier for Verifier { + type Error = transaction::Error; + type VerifiedTransaction = VerifiedTransaction; + + fn verify_transaction(&self, tx: Transaction) -> Result { + // The checks here should be ordered by cost/complexity. + // Cheap checks should be done as early as possible to discard unneeded transactions early. + + let hash = tx.hash(); + + if self.client.transaction_already_included(&hash) { + trace!(target: "txqueue", "[{:?}] Rejected tx already in the blockchain", hash); + bail!(transaction::Error::AlreadyImported) + } + + let gas_limit = cmp::min(self.options.tx_gas_limit, self.options.block_gas_limit); + if tx.gas() > &gas_limit { + debug!( + target: "txqueue", + "[{:?}] Dropping transaction above gas limit: {} > min({}, {})", + hash, + tx.gas(), + self.options.block_gas_limit, + self.options.tx_gas_limit, + ); + bail!(transaction::Error::GasLimitExceeded { + limit: gas_limit, + got: *tx.gas(), + }); + } + + let minimal_gas = self.client.required_gas(tx.transaction()); + if tx.gas() < &minimal_gas { + trace!(target: "txqueue", + "[{:?}] Dropping transaction with insufficient gas: {} < {}", + hash, + tx.gas(), + minimal_gas, + ); + + bail!(transaction::Error::InsufficientGas { + minimal: minimal_gas, + got: *tx.gas(), + }) + } + + let is_own = tx.is_local(); + // Quick exit for non-service transactions + if tx.gas_price() < &self.options.minimal_gas_price + && !tx.gas_price().is_zero() + && !is_own + { + trace!( + target: "txqueue", + "[{:?}] Rejected tx below minimal gas price threshold: {} < {}", + hash, + tx.gas_price(), + self.options.minimal_gas_price, + ); + bail!(transaction::Error::InsufficientGasPrice { + minimal: self.options.minimal_gas_price, + got: *tx.gas_price(), + }); + } + + // Some more heavy checks below. + // Actually recover sender and verify that transaction + let is_retracted = tx.is_retracted(); + let transaction = match tx { + Transaction::Retracted(tx) | Transaction::Unverified(tx) => match self.client.verify_transaction(tx) { + Ok(signed) => signed.into(), + Err(err) => { + debug!(target: "txqueue", "[{:?}] Rejected tx {:?}", hash, err); + bail!(err) + }, + }, + Transaction::Local(tx) => tx, + }; + + // Verify RLP payload + if let Err(err) = self.client.decode_transaction(&transaction.rlp_bytes()) { + debug!(target: "txqueue", "[{:?}] Rejected transaction's rlp payload", err); + bail!(err) + } + + let sender = transaction.sender(); + let account_details = self.client.account_details(&sender); + + if transaction.gas_price < self.options.minimal_gas_price { + let transaction_type = self.client.transaction_type(&transaction); + if let TransactionType::Service = transaction_type { + debug!(target: "txqueue", "Service tx {:?} below minimal gas price accepted", hash); + } else if is_own || account_details.is_local { + info!(target: "own_tx", "Local tx {:?} below minimal gas price accepted", hash); + } else { + trace!( + target: "txqueue", + "[{:?}] Rejected tx below minimal gas price threshold: {} < {}", + hash, + transaction.gas_price, + self.options.minimal_gas_price, + ); + bail!(transaction::Error::InsufficientGasPrice { + minimal: self.options.minimal_gas_price, + got: transaction.gas_price, + }); + } + } + + let cost = transaction.value + transaction.gas_price * transaction.gas; + if account_details.balance < cost { + debug!( + target: "txqueue", + "[{:?}] Rejected tx with not enough balance: {} < {}", + hash, + account_details.balance, + cost, + ); + bail!(transaction::Error::InsufficientBalance { + cost: cost, + balance: account_details.balance, + }); + } + + if transaction.nonce < account_details.nonce { + debug!( + target: "txqueue", + "[{:?}] Rejected tx with old nonce ({} < {})", + hash, + transaction.nonce, + account_details.nonce, + ); + bail!(transaction::Error::Old); + } + + let priority = match (is_own || account_details.is_local, is_retracted) { + (true, _) => super::Priority::Local, + (false, false) => super::Priority::Regular, + (false, true) => super::Priority::Retracted, + }; + Ok(VerifiedTransaction { + transaction, + priority, + hash, + sender, + insertion_id: self.id.fetch_add(1, atomic::Ordering::AcqRel), + }) + } +} diff --git a/miner/src/transaction_queue.rs b/miner/src/transaction_queue.rs deleted file mode 100644 index 2be7fc8a3b8597cbd35b80bb641b1ae0db972159..0000000000000000000000000000000000000000 --- a/miner/src/transaction_queue.rs +++ /dev/null @@ -1,2944 +0,0 @@ -// Copyright 2015-2017 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 . - -//! Transaction Queue -//! -//! `TransactionQueue` keeps track of all transactions seen by the node (received from other peers) and own transactions -//! and orders them by priority. Top priority transactions are those with low nonce height (difference between -//! transaction's nonce and next nonce expected from this sender). If nonces are equal transaction's gas price is used -//! for comparison (higher gas price = higher priority). -//! -//! # Usage Example -//! -//! ```rust -//! extern crate ethereum_types; -//! extern crate ethcore_miner as miner; -//! extern crate ethcore_transaction as transaction; -//! extern crate ethkey; -//! extern crate rustc_hex; -//! -//! use ethereum_types::{U256, Address}; -//! use ethkey::{Random, Generator}; -//! use miner::transaction_queue::{TransactionQueue, TransactionDetailsProvider, AccountDetails, TransactionOrigin, RemovalReason}; -//! use transaction::*; -//! use rustc_hex::FromHex; -//! -//! #[derive(Default)] -//! struct DummyTransactionDetailsProvider; -//! -//! impl TransactionDetailsProvider for DummyTransactionDetailsProvider { -//! fn fetch_account(&self, _address: &Address) -> AccountDetails { -//! AccountDetails { -//! nonce: U256::from(10), -//! balance: U256::from(1_000_000) -//! } -//! } -//! -//! fn estimate_gas_required(&self, _tx: &SignedTransaction) -> U256 { -//! 2.into() -//! } -//! -//! fn is_service_transaction_acceptable(&self, _tx: &SignedTransaction) -> Result { -//! Ok(true) -//! } -//! } -//! -//! fn main() { -//! let key = Random.generate().unwrap(); -//! let t1 = Transaction { action: Action::Create, value: U256::from(100), data: "3331600055".from_hex().unwrap(), -//! gas: U256::from(100_000), gas_price: U256::one(), nonce: U256::from(10) }; -//! let t2 = Transaction { action: Action::Create, value: U256::from(100), data: "3331600055".from_hex().unwrap(), -//! gas: U256::from(100_000), gas_price: U256::one(), nonce: U256::from(11) }; -//! -//! let st1 = t1.sign(&key.secret(), None); -//! let st2 = t2.sign(&key.secret(), None); -//! let details_provider = DummyTransactionDetailsProvider::default(); -//! -//! let mut txq = TransactionQueue::default(); -//! txq.add(st2.clone(), TransactionOrigin::External, 0, None, &details_provider).unwrap(); -//! txq.add(st1.clone(), TransactionOrigin::External, 0, None, &details_provider).unwrap(); -//! -//! // Check status -//! assert_eq!(txq.status().pending, 2); -//! // Check top transactions -//! let top = txq.top_transactions(); -//! assert_eq!(top.len(), 2); -//! assert_eq!(top[0], st1); -//! assert_eq!(top[1], st2); -//! -//! // And when transaction is removed (but nonce haven't changed) -//! // it will move subsequent transactions to future -//! txq.remove(&st1.hash(), &|_| 10.into(), RemovalReason::Invalid); -//! assert_eq!(txq.status().pending, 0); -//! assert_eq!(txq.status().future, 1); -//! assert_eq!(txq.top_transactions().len(), 0); -//! } -//! ``` -//! -//! # Maintaing valid state -//! -//! 1. Whenever transaction is imported to queue (to queue) all other transactions from this sender are revalidated in current. It means that they are moved to future and back again (height recalculation & gap filling). -//! 2. Whenever invalid transaction is removed: -//! - When it's removed from `future` - all `future` transactions heights are recalculated and then -//! we check if the transactions should go to `current` (comparing state nonce) -//! - When it's removed from `current` - all transactions from this sender (`current` & `future`) are recalculated. -//! 3. `cull` is used to inform the queue about client (state) nonce changes. -//! - It removes all transactions (either from `current` or `future`) with nonce < client nonce -//! - It moves matching `future` transactions to `current` -//! 4. `remove_old` is used as convenient method to update the state nonce for all senders in the queue. -//! - Invokes `cull` with latest state nonce for all senders. - -use std::cmp::Ordering; -use std::cmp; -use std::collections::{HashSet, HashMap, BTreeSet, BTreeMap}; -use std::ops::Deref; - -use ethereum_types::{H256, U256, Address}; -use heapsize::HeapSizeOf; -use linked_hash_map::LinkedHashMap; -use local_transactions::{LocalTransactionsList, Status as LocalTransactionStatus}; -use table::Table; -use transaction::{self, SignedTransaction, PendingTransaction}; - -type BlockNumber = u64; - -/// Transaction origin -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum TransactionOrigin { - /// Transaction coming from local RPC - Local, - /// External transaction received from network - External, - /// Transactions from retracted blocks - RetractedBlock, -} - -impl PartialOrd for TransactionOrigin { - fn partial_cmp(&self, other: &TransactionOrigin) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for TransactionOrigin { - fn cmp(&self, other: &TransactionOrigin) -> Ordering { - if *other == *self { - return Ordering::Equal; - } - - match (*self, *other) { - (TransactionOrigin::RetractedBlock, _) => Ordering::Less, - (_, TransactionOrigin::RetractedBlock) => Ordering::Greater, - (TransactionOrigin::Local, _) => Ordering::Less, - _ => Ordering::Greater, - } - } -} - -impl TransactionOrigin { - fn is_local(&self) -> bool { - *self == TransactionOrigin::Local - } -} - -#[derive(Clone, Debug)] -/// Light structure used to identify transaction and its order -struct TransactionOrder { - /// Primary ordering factory. Difference between transaction nonce and expected nonce in state - /// (e.g. Tx(nonce:5), State(nonce:0) -> height: 5) - /// High nonce_height = Low priority (processed later) - nonce_height: U256, - /// Gas Price of the transaction. - /// Low gas price = Low priority (processed later) - gas_price: U256, - /// Gas usage priority factor. Usage depends on strategy. - /// Represents the linear increment in required gas price for heavy transactions. - /// - /// High gas limit + Low gas price = Very Low priority - /// High gas limit + High gas price = High priority - gas_factor: U256, - /// Gas (limit) of the transaction. Usage depends on strategy. - /// Low gas limit = High priority (processed earlier) - gas: U256, - /// Heap usage of this transaction. - mem_usage: usize, - /// Transaction ordering strategy - strategy: PrioritizationStrategy, - /// Hash to identify associated transaction - hash: H256, - /// Incremental id assigned when transaction is inserted to the queue. - insertion_id: u64, - /// Origin of the transaction - origin: TransactionOrigin, - /// Penalties - penalties: usize, -} - - -impl TransactionOrder { - - fn for_transaction(tx: &VerifiedTransaction, base_nonce: U256, min_gas_price: U256, strategy: PrioritizationStrategy) -> Self { - let factor = (tx.transaction.gas >> 15) * min_gas_price; - TransactionOrder { - nonce_height: tx.nonce() - base_nonce, - gas_price: tx.transaction.gas_price, - gas_factor: factor, - gas: tx.transaction.gas, - mem_usage: tx.transaction.heap_size_of_children(), - strategy: strategy, - hash: tx.hash(), - insertion_id: tx.insertion_id, - origin: tx.origin, - penalties: 0, - } - } - - fn update_height(mut self, nonce: U256, base_nonce: U256) -> Self { - self.nonce_height = nonce - base_nonce; - self - } - - fn penalize(mut self) -> Self { - self.penalties = self.penalties.saturating_add(1); - self - } -} - -impl Eq for TransactionOrder {} -impl PartialEq for TransactionOrder { - fn eq(&self, other: &TransactionOrder) -> bool { - self.cmp(other) == Ordering::Equal - } -} -impl PartialOrd for TransactionOrder { - fn partial_cmp(&self, other: &TransactionOrder) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for TransactionOrder { - fn cmp(&self, b: &TransactionOrder) -> Ordering { - // First check number of penalties - if self.penalties != b.penalties { - return self.penalties.cmp(&b.penalties); - } - - // Local transactions should always have priority - if self.origin != b.origin { - return self.origin.cmp(&b.origin); - } - - // Check nonce_height - if self.nonce_height != b.nonce_height { - return self.nonce_height.cmp(&b.nonce_height); - } - - match self.strategy { - PrioritizationStrategy::GasAndGasPrice => { - if self.gas != b.gas { - return self.gas.cmp(&b.gas); - } - }, - PrioritizationStrategy::GasFactorAndGasPrice => { - // avoiding overflows - // (gp1 - g1) > (gp2 - g2) <=> - // (gp1 + g2) > (gp2 + g1) - let f_a = self.gas_price + b.gas_factor; - let f_b = b.gas_price + self.gas_factor; - if f_a != f_b { - return f_b.cmp(&f_a); - } - }, - PrioritizationStrategy::GasPriceOnly => {}, - } - - // Then compare gas_prices - if self.gas_price != b.gas_price { - return b.gas_price.cmp(&self.gas_price); - } - - // Lastly compare insertion_id - self.insertion_id.cmp(&b.insertion_id) - } -} - -/// Verified transaction -#[derive(Debug)] -struct VerifiedTransaction { - /// Transaction. - transaction: SignedTransaction, - /// Transaction origin. - origin: TransactionOrigin, - /// Delay until specified condition is met. - condition: Option, - /// Insertion time - insertion_time: QueuingInstant, - /// ID assigned upon insertion, should be unique. - insertion_id: u64, -} - -impl VerifiedTransaction { - fn new( - transaction: SignedTransaction, - origin: TransactionOrigin, - condition: Option, - insertion_time: QueuingInstant, - insertion_id: u64, - ) -> Self { - VerifiedTransaction { - transaction, - origin, - condition, - insertion_time, - insertion_id, - } - } - - fn hash(&self) -> H256 { - self.transaction.hash() - } - - fn nonce(&self) -> U256 { - self.transaction.nonce - } - - fn sender(&self) -> Address { - self.transaction.sender() - } - - fn cost(&self) -> U256 { - self.transaction.value + self.transaction.gas_price * self.transaction.gas - } -} - -#[derive(Debug, Default)] -struct GasPriceQueue { - backing: BTreeMap>, -} - -impl GasPriceQueue { - /// Insert an item into a BTreeMap/HashSet "multimap". - pub fn insert(&mut self, gas_price: U256, hash: H256) -> bool { - self.backing.entry(gas_price).or_insert_with(Default::default).insert(hash) - } - - /// Remove an item from a BTreeMap/HashSet "multimap". - /// Returns true if the item was removed successfully. - pub fn remove(&mut self, gas_price: &U256, hash: &H256) -> bool { - if let Some(hashes) = self.backing.get_mut(gas_price) { - let only_one_left = hashes.len() == 1; - if !only_one_left { - // Operation may be ok: only if hash is in gas-price's Set. - return hashes.remove(hash); - } - if hash != hashes.iter().next().expect("We know there is only one element in collection, tested above; qed") { - // Operation failed: hash not the single item in gas-price's Set. - return false; - } - } else { - // Operation failed: gas-price not found in Map. - return false; - } - // Operation maybe ok: only if hash not found in gas-price Set. - self.backing.remove(gas_price).is_some() - } -} - -impl Deref for GasPriceQueue { - type Target=BTreeMap>; - - fn deref(&self) -> &Self::Target { - &self.backing - } -} - -/// Holds transactions accessible by (address, nonce) and by priority -/// -/// `TransactionSet` keeps number of entries below limit, but it doesn't -/// automatically happen during `insert/remove` operations. -/// You have to call `enforce_limit` to remove lowest priority transactions from set. -struct TransactionSet { - by_priority: BTreeSet, - by_address: Table, - by_gas_price: GasPriceQueue, - limit: usize, - total_gas_limit: U256, - memory_limit: usize, -} - -impl TransactionSet { - /// Inserts `TransactionOrder` to this set. Transaction does not need to be unique - - /// the same transaction may be validly inserted twice. Any previous transaction that - /// it replaces (i.e. with the same `sender` and `nonce`) should be returned. - fn insert(&mut self, sender: Address, nonce: U256, order: TransactionOrder) -> Option { - if !self.by_priority.insert(order.clone()) { - return Some(order.clone()); - } - let order_hash = order.hash.clone(); - let order_gas_price = order.gas_price.clone(); - let by_address_replaced = self.by_address.insert(sender, nonce, order); - // If transaction was replaced remove it from priority queue - if let Some(ref old_order) = by_address_replaced { - assert!(self.by_priority.remove(old_order), "hash is in `by_address`; all transactions in `by_address` must be in `by_priority`; qed"); - assert!(self.by_gas_price.remove(&old_order.gas_price, &old_order.hash), - "hash is in `by_address`; all transactions' gas_prices in `by_address` must be in `by_gas_limit`; qed"); - } - self.by_gas_price.insert(order_gas_price, order_hash); - assert_eq!(self.by_priority.len(), self.by_address.len()); - assert_eq!(self.by_gas_price.values().map(|v| v.len()).fold(0, |a, b| a + b), self.by_address.len()); - by_address_replaced - } - - /// Remove low priority transactions if there is more than specified by given `limit`. - /// - /// It drops transactions from this set but also removes associated `VerifiedTransaction`. - /// Returns addresses and lowest nonces of transactions removed because of limit. - fn enforce_limit(&mut self, by_hash: &mut HashMap, local: &mut LocalTransactionsList) -> Option> { - let mut count = 0; - let mut mem_usage = 0; - let mut gas: U256 = 0.into(); - let to_drop : Vec<(Address, U256)> = { - self.by_priority - .iter() - .filter(|order| { - // update transaction count and mem usage - count += 1; - mem_usage += order.mem_usage; - - // calculate current gas usage - let r = gas.overflowing_add(order.gas); - if r.1 { return false } - gas = r.0; - - let is_own_or_retracted = order.origin.is_local() || order.origin == TransactionOrigin::RetractedBlock; - // Own and retracted transactions are allowed to go above all limits. - !is_own_or_retracted && (mem_usage > self.memory_limit || count > self.limit || gas > self.total_gas_limit) - }) - .map(|order| by_hash.get(&order.hash) - .expect("All transactions in `self.by_priority` and `self.by_address` are kept in sync with `by_hash`.")) - .map(|tx| (tx.sender(), tx.nonce())) - .collect() - }; - - Some(to_drop.into_iter() - .fold(HashMap::new(), |mut removed, (sender, nonce)| { - let order = self.drop(&sender, &nonce) - .expect("Transaction has just been found in `by_priority`; so it is in `by_address` also."); - trace!(target: "txqueue", "Dropped out of limit transaction: {:?}", order.hash); - - let order = by_hash.remove(&order.hash) - .expect("hash is in `by_priorty`; all hashes in `by_priority` must be in `by_hash`; qed"); - - if order.origin.is_local() { - local.mark_dropped(order.transaction); - } - - let min = removed.get(&sender).map_or(nonce, |val| cmp::min(*val, nonce)); - removed.insert(sender, min); - removed - })) - } - - /// Drop transaction from this set (remove from `by_priority` and `by_address`) - fn drop(&mut self, sender: &Address, nonce: &U256) -> Option { - if let Some(tx_order) = self.by_address.remove(sender, nonce) { - assert!(self.by_gas_price.remove(&tx_order.gas_price, &tx_order.hash), - "hash is in `by_address`; all transactions' gas_prices in `by_address` must be in `by_gas_limit`; qed"); - assert!(self.by_priority.remove(&tx_order), - "hash is in `by_address`; all transactions' gas_prices in `by_address` must be in `by_priority`; qed"); - assert_eq!(self.by_priority.len(), self.by_address.len()); - assert_eq!(self.by_gas_price.values().map(|v| v.len()).fold(0, |a, b| a + b), self.by_address.len()); - return Some(tx_order); - } - assert_eq!(self.by_priority.len(), self.by_address.len()); - assert_eq!(self.by_gas_price.values().map(|v| v.len()).fold(0, |a, b| a + b), self.by_address.len()); - None - } - - /// Drop all transactions. - fn clear(&mut self) { - self.by_priority.clear(); - self.by_address.clear(); - self.by_gas_price.backing.clear(); - } - - /// Sets new limit for number of transactions in this `TransactionSet`. - /// Note the limit is not applied (no transactions are removed) by calling this method. - fn set_limit(&mut self, limit: usize) { - self.limit = limit; - } - - /// Get the minimum gas price that we can accept into this queue that wouldn't cause the transaction to - /// immediately be dropped. 0 if the queue isn't at capacity; 1 plus the lowest if it is. - fn gas_price_entry_limit(&self) -> U256 { - match self.by_gas_price.keys().next() { - Some(k) if self.by_priority.len() >= self.limit => *k + 1.into(), - _ => U256::default(), - } - } -} - -#[derive(Debug)] -/// Current status of the queue -pub struct TransactionQueueStatus { - /// Number of pending transactions (ready to go to block) - pub pending: usize, - /// Number of future transactions (waiting for transactions with lower nonces first) - pub future: usize, -} - -/// Details of account -pub struct AccountDetails { - /// Most recent account nonce - pub nonce: U256, - /// Current account balance - pub balance: U256, -} - -/// Transaction with the same (sender, nonce) can be replaced only if -/// `new_gas_price > old_gas_price + old_gas_price >> SHIFT` -const GAS_PRICE_BUMP_SHIFT: usize = 3; // 2 = 25%, 3 = 12.5%, 4 = 6.25% - -/// Describes the strategy used to prioritize transactions in the queue. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum PrioritizationStrategy { - /// Use only gas price. Disregards the actual computation cost of the transaction. - /// i.e. Higher gas price = Higher priority - GasPriceOnly, - /// Use gas limit and then gas price. - /// i.e. Higher gas limit = Lower priority - GasAndGasPrice, - /// Calculate and use priority based on gas and gas price. - /// PRIORITY = GAS_PRICE - GAS/2^15 * MIN_GAS_PRICE - /// - /// Rationale: - /// Heavy transactions are paying linear cost (GAS * GAS_PRICE) - /// while the computation might be more expensive. - /// - /// i.e. - /// 1M gas tx with `gas_price=30*min` has the same priority - /// as 32k gas tx with `gas_price=min` - GasFactorAndGasPrice, -} - -/// Reason to remove single transaction from the queue. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum RemovalReason { - /// Transaction is invalid - Invalid, - /// Transaction was canceled - Canceled, - /// Transaction is not allowed, - NotAllowed, -} - -/// Point in time when transaction was inserted. -pub type QueuingInstant = BlockNumber; -const DEFAULT_QUEUING_PERIOD: BlockNumber = 128; - -/// `TransactionQueue` transaction details provider. -pub trait TransactionDetailsProvider { - /// Fetch transaction-related account details. - fn fetch_account(&self, address: &Address) -> AccountDetails; - /// Estimate gas required for transaction. - fn estimate_gas_required(&self, tx: &SignedTransaction) -> U256; - /// Check if this service transaction can be accepted by `TransactionQueue`. - fn is_service_transaction_acceptable(&self, tx: &SignedTransaction) -> Result; -} - -/// `TransactionQueue` implementation -pub struct TransactionQueue { - /// Prioritization strategy for this queue - strategy: PrioritizationStrategy, - /// Gas Price threshold for transactions that can be imported to this queue (defaults to 0) - minimal_gas_price: U256, - /// The maximum amount of gas any individual transaction may use. - tx_gas_limit: U256, - /// Current gas limit (block gas limit). Transactions above the limit will not be accepted (default to !0) - block_gas_limit: U256, - /// Maximal time transaction may occupy the queue. - /// When we reach `max_time_in_queue / 2^3` we re-validate - /// account balance. - max_time_in_queue: QueuingInstant, - /// Priority queue for transactions that can go to block - current: TransactionSet, - /// Priority queue for transactions that has been received but are not yet valid to go to block - future: TransactionSet, - /// All transactions managed by queue indexed by hash - by_hash: HashMap, - /// Last nonce of transaction in current (to quickly check next expected transaction) - last_nonces: HashMap, - /// List of local transactions and their statuses. - local_transactions: LocalTransactionsList, - /// Next id that should be assigned to a transaction imported to the queue. - next_transaction_id: u64, -} - -impl Default for TransactionQueue { - fn default() -> Self { - TransactionQueue::new(PrioritizationStrategy::GasPriceOnly) - } -} - -impl TransactionQueue { - /// Creates new instance of this Queue - pub fn new(strategy: PrioritizationStrategy) -> Self { - Self::with_limits(strategy, 8192, usize::max_value(), !U256::zero(), !U256::zero()) - } - - /// Create new instance of this Queue with specified limits - pub fn with_limits( - strategy: PrioritizationStrategy, - limit: usize, - memory_limit: usize, - total_gas_limit: U256, - tx_gas_limit: U256, - ) -> Self { - let current = TransactionSet { - by_priority: BTreeSet::new(), - by_address: Table::new(), - by_gas_price: Default::default(), - limit, - total_gas_limit, - memory_limit, - }; - - let future = TransactionSet { - by_priority: BTreeSet::new(), - by_address: Table::new(), - by_gas_price: Default::default(), - total_gas_limit, - limit, - memory_limit, - }; - - TransactionQueue { - strategy, - minimal_gas_price: U256::zero(), - block_gas_limit: !U256::zero(), - tx_gas_limit, - max_time_in_queue: DEFAULT_QUEUING_PERIOD, - current, - future, - by_hash: HashMap::new(), - last_nonces: HashMap::new(), - local_transactions: LocalTransactionsList::default(), - next_transaction_id: 0, - } - } - - /// Set the new limit for `current` and `future` queue. - pub fn set_limit(&mut self, limit: usize) { - self.current.set_limit(limit); - self.future.set_limit(limit); - // And ensure the limits - self.current.enforce_limit(&mut self.by_hash, &mut self.local_transactions); - self.future.enforce_limit(&mut self.by_hash, &mut self.local_transactions); - } - - /// Returns current limit of transactions in the queue. - pub fn limit(&self) -> usize { - self.current.limit - } - - /// Get the minimal gas price. - pub fn minimal_gas_price(&self) -> &U256 { - &self.minimal_gas_price - } - - /// Sets new gas price threshold for incoming transactions. - /// Any transaction already imported to the queue is not affected. - pub fn set_minimal_gas_price(&mut self, min_gas_price: U256) { - self.minimal_gas_price = min_gas_price; - } - - /// Get one more than the lowest gas price in the queue iff the pool is - /// full, otherwise 0. - pub fn effective_minimum_gas_price(&self) -> U256 { - self.current.gas_price_entry_limit() - } - - /// Sets new gas limit. Transactions with gas over the limit will not be accepted. - /// Any transaction already imported to the queue is not affected. - pub fn set_gas_limit(&mut self, gas_limit: U256) { - self.block_gas_limit = gas_limit; - } - - /// Sets new total gas limit. - pub fn set_total_gas_limit(&mut self, total_gas_limit: U256) { - self.current.total_gas_limit = total_gas_limit; - self.future.total_gas_limit = total_gas_limit; - self.future.enforce_limit(&mut self.by_hash, &mut self.local_transactions); - } - - /// Set the new limit for the amount of gas any individual transaction may have. - /// Any transaction already imported to the queue is not affected. - pub fn set_tx_gas_limit(&mut self, limit: U256) { - self.tx_gas_limit = limit; - } - - /// Returns current status for this queue - pub fn status(&self) -> TransactionQueueStatus { - TransactionQueueStatus { - pending: self.current.by_priority.len(), - future: self.future.by_priority.len(), - } - } - - /// Add signed transaction to queue to be verified and imported. - /// - /// NOTE details_provider methods should be cheap to compute - /// otherwise it might open up an attack vector. - pub fn add( - &mut self, - tx: SignedTransaction, - origin: TransactionOrigin, - time: QueuingInstant, - condition: Option, - details_provider: &TransactionDetailsProvider, - ) -> Result { - if origin == TransactionOrigin::Local { - let hash = tx.hash(); - let cloned_tx = tx.clone(); - - let result = self.add_internal(tx, origin, time, condition, details_provider); - match result { - Ok(transaction::ImportResult::Current) => { - self.local_transactions.mark_pending(hash); - }, - Ok(transaction::ImportResult::Future) => { - self.local_transactions.mark_future(hash); - }, - Err(ref err) => { - // Sometimes transactions are re-imported, so - // don't overwrite transactions if they are already on the list - if !self.local_transactions.contains(&hash) { - self.local_transactions.mark_rejected(cloned_tx, err.clone()); - } - }, - } - result - } else { - self.add_internal(tx, origin, time, condition, details_provider) - } - } - - /// Adds signed transaction to the queue. - fn add_internal( - &mut self, - tx: SignedTransaction, - origin: TransactionOrigin, - time: QueuingInstant, - condition: Option, - details_provider: &TransactionDetailsProvider, - ) -> Result { - if origin != TransactionOrigin::Local && tx.gas_price < self.minimal_gas_price { - // if it is non-service-transaction => drop - let is_service_transaction = tx.gas_price.is_zero(); - if !is_service_transaction { - trace!(target: "txqueue", - "Dropping transaction below minimal gas price threshold: {:?} (gp: {} < {})", - tx.hash(), - tx.gas_price, - self.minimal_gas_price - ); - - return Err(transaction::Error::InsufficientGasPrice { - minimal: self.minimal_gas_price, - got: tx.gas_price, - }); - } - - let is_service_transaction_accepted = match details_provider.is_service_transaction_acceptable(&tx) { - Ok(true) => true, - Ok(false) => { - trace!(target: "txqueue", - "Dropping service transaction as sender is not certified to send service transactions: {:?} (sender: {:?})", - tx.hash(), - tx.sender(), - ); - - false - }, - Err(contract_err) => { - trace!(target: "txqueue", - "Dropping service transaction as service contract returned error: {:?} (error: {:?})", - tx.hash(), - contract_err, - ); - - false - }, - }; - - if !is_service_transaction_accepted { - return Err(transaction::Error::InsufficientGasPrice { - minimal: self.minimal_gas_price, - got: tx.gas_price, - }); - } - } - - let full_queues_lowest = self.effective_minimum_gas_price(); - if tx.gas_price < full_queues_lowest && origin != TransactionOrigin::Local { - trace!(target: "txqueue", - "Dropping transaction below lowest gas price in a full queue: {:?} (gp: {} < {})", - tx.hash(), - tx.gas_price, - full_queues_lowest - ); - - return Err(transaction::Error::InsufficientGasPrice { - minimal: full_queues_lowest, - got: tx.gas_price, - }); - } - - let gas_limit = cmp::min(self.tx_gas_limit, self.block_gas_limit); - if tx.gas > gas_limit { - trace!(target: "txqueue", - "Dropping transaction above gas limit: {:?} ({} > min({}, {}))", - tx.hash(), - tx.gas, - self.block_gas_limit, - self.tx_gas_limit - ); - return Err(transaction::Error::GasLimitExceeded { - limit: gas_limit, - got: tx.gas, - }); - } - - let minimal_gas = details_provider.estimate_gas_required(&tx); - if tx.gas < minimal_gas { - trace!(target: "txqueue", - "Dropping transaction with insufficient gas: {:?} ({} > {})", - tx.hash(), - tx.gas, - minimal_gas, - ); - - return Err(transaction::Error::InsufficientGas { - minimal: minimal_gas, - got: tx.gas, - }); - } - - let client_account = details_provider.fetch_account(&tx.sender()); - let cost = tx.value + tx.gas_price * tx.gas; - if client_account.balance < cost { - trace!(target: "txqueue", - "Dropping transaction without sufficient balance: {:?} ({} < {})", - tx.hash(), - client_account.balance, - cost - ); - - return Err(transaction::Error::InsufficientBalance { - cost: cost, - balance: client_account.balance - }); - } - tx.check_low_s()?; - // No invalid transactions beyond this point. - let id = self.next_transaction_id; - self.next_transaction_id += 1; - let vtx = VerifiedTransaction::new(tx, origin, condition, time, id); - let r = self.import_tx(vtx, client_account.nonce); - assert_eq!(self.future.by_priority.len() + self.current.by_priority.len(), self.by_hash.len()); - r - } - - /// Removes all transactions from particular sender up to (excluding) given client (state) nonce. - /// Client (State) Nonce = next valid nonce for this sender. - pub fn cull(&mut self, sender: Address, client_nonce: U256) { - // Check if there is anything in current... - let should_check_in_current = self.current.by_address.row(&sender) - // If nonce == client_nonce nothing is changed - .and_then(|by_nonce| by_nonce.keys().find(|nonce| *nonce < &client_nonce)) - .map(|_| ()); - // ... or future - let should_check_in_future = self.future.by_address.row(&sender) - // if nonce == client_nonce we need to promote to current - .and_then(|by_nonce| by_nonce.keys().find(|nonce| *nonce <= &client_nonce)) - .map(|_| ()); - - if should_check_in_current.or(should_check_in_future).is_none() { - return; - } - - self.cull_internal(sender, client_nonce); - } - - /// Always updates future and moves transactions from current to future. - fn cull_internal(&mut self, sender: Address, client_nonce: U256) { - // We will either move transaction to future or remove it completely - // so there will be no transactions from this sender in current - self.last_nonces.remove(&sender); - // First update height of transactions in future to avoid collisions - self.update_future(&sender, client_nonce); - // This should move all current transactions to future and remove old transactions - self.move_all_to_future(&sender, client_nonce); - // And now lets check if there is some batch of transactions in future - // that should be placed in current. It should also update last_nonces. - self.move_matching_future_to_current(sender, client_nonce, client_nonce); - assert_eq!(self.future.by_priority.len() + self.current.by_priority.len(), self.by_hash.len()); - } - - /// Checks the current nonce for all transactions' senders in the queue and removes the old transactions. - pub fn remove_old(&mut self, fetch_account: &F, current_time: QueuingInstant) where - F: Fn(&Address) -> AccountDetails, - { - let senders = self.current.by_address.keys() - .chain(self.future.by_address.keys()) - .map(|sender| (*sender, fetch_account(sender))) - .collect::>(); - - for (sender, details) in senders.iter() { - self.cull(*sender, details.nonce); - } - - let max_time = self.max_time_in_queue; - let balance_check = max_time >> 3; - // Clear transactions occupying the queue too long - let invalid = self.by_hash.iter() - .filter(|&(_, ref tx)| !tx.origin.is_local()) - .map(|(hash, tx)| (hash, tx, current_time.saturating_sub(tx.insertion_time))) - .filter_map(|(hash, tx, time_diff)| { - if time_diff > max_time { - return Some(*hash); - } - - if time_diff > balance_check { - return match senders.get(&tx.sender()) { - Some(details) if tx.cost() > details.balance => { - Some(*hash) - }, - _ => None, - }; - } - - None - }) - .collect::>(); - let fetch_nonce = |a: &Address| senders.get(a) - .expect("We fetch details for all senders from both current and future") - .nonce; - for hash in invalid { - self.remove(&hash, &fetch_nonce, RemovalReason::Invalid); - } - } - - /// Penalize transactions from sender of transaction with given hash. - /// I.e. it should change the priority of the transaction in the queue. - /// - /// NOTE: We need to penalize all transactions from particular sender - /// to avoid breaking invariants in queue (ordered by nonces). - /// Consecutive transactions from this sender would fail otherwise (because of invalid nonce). - pub fn penalize(&mut self, transaction_hash: &H256) { - let transaction = match self.by_hash.get(transaction_hash) { - None => return, - Some(t) => t, - }; - - // Never penalize local transactions - if transaction.origin.is_local() { - return; - } - - let sender = transaction.sender(); - - // Penalize all transactions from this sender - let nonces_from_sender = match self.current.by_address.row(&sender) { - Some(row_map) => row_map.keys().cloned().collect::>(), - None => vec![], - }; - for k in nonces_from_sender { - let order = self.current.drop(&sender, &k).expect("transaction known to be in self.current; qed"); - self.current.insert(sender, k, order.penalize()); - } - // Same thing for future - let nonces_from_sender = match self.future.by_address.row(&sender) { - Some(row_map) => row_map.keys().cloned().collect::>(), - None => vec![], - }; - for k in nonces_from_sender { - let order = self.future.drop(&sender, &k).expect("transaction known to be in self.future; qed"); - self.future.insert(sender, k, order.penalize()); - } - } - - /// Removes invalid transaction identified by hash from queue. - /// Assumption is that this transaction nonce is not related to client nonce, - /// so transactions left in queue are processed according to client nonce. - /// - /// If gap is introduced marks subsequent transactions as future - pub fn remove(&mut self, transaction_hash: &H256, fetch_nonce: &F, reason: RemovalReason) - where F: Fn(&Address) -> U256 { - - assert_eq!(self.future.by_priority.len() + self.current.by_priority.len(), self.by_hash.len()); - let transaction = self.by_hash.remove(transaction_hash); - if transaction.is_none() { - // We don't know this transaction - return; - } - - let transaction = transaction.expect("None is tested in early-exit condition above; qed"); - let sender = transaction.sender(); - let nonce = transaction.nonce(); - let current_nonce = fetch_nonce(&sender); - - trace!(target: "txqueue", "Removing invalid transaction: {:?}", transaction.hash()); - - // Mark in locals - if self.local_transactions.contains(transaction_hash) { - match reason { - RemovalReason::Invalid => self.local_transactions.mark_invalid( - transaction.transaction.into() - ), - RemovalReason::NotAllowed => self.local_transactions.mark_invalid( - transaction.transaction.into() - ), - RemovalReason::Canceled => self.local_transactions.mark_canceled( - PendingTransaction::new(transaction.transaction, transaction.condition) - ), - } - } - - // Remove from future - let order = self.future.drop(&sender, &nonce); - if order.is_some() { - self.update_future(&sender, current_nonce); - // And now lets check if there is some chain of transactions in future - // that should be placed in current - self.move_matching_future_to_current(sender, current_nonce, current_nonce); - assert_eq!(self.future.by_priority.len() + self.current.by_priority.len(), self.by_hash.len()); - return; - } - - // Remove from current - let order = self.current.drop(&sender, &nonce); - if order.is_some() { - // This will keep consistency in queue - // Moves all to future and then promotes a batch from current: - self.cull_internal(sender, current_nonce); - assert_eq!(self.future.by_priority.len() + self.current.by_priority.len(), self.by_hash.len()); - return; - } - } - - /// Marks all transactions from particular sender as local transactions - fn mark_transactions_local(&mut self, sender: &Address) { - fn mark_local(sender: &Address, set: &mut TransactionSet, mut mark: F) { - // Mark all transactions from this sender as local - let nonces_from_sender = set.by_address.row(sender) - .map(|row_map| { - row_map.iter().filter_map(|(nonce, order)| if order.origin.is_local() { - None - } else { - Some(*nonce) - }).collect::>() - }) - .unwrap_or_else(Vec::new); - - for k in nonces_from_sender { - let mut order = set.drop(sender, &k).expect("transaction known to be in self.current/self.future; qed"); - order.origin = TransactionOrigin::Local; - mark(order.hash); - set.insert(*sender, k, order); - } - } - - let local = &mut self.local_transactions; - mark_local(sender, &mut self.current, |hash| local.mark_pending(hash)); - mark_local(sender, &mut self.future, |hash| local.mark_future(hash)); - } - - /// Update height of all transactions in future transactions set. - fn update_future(&mut self, sender: &Address, current_nonce: U256) { - // We need to drain all transactions for current sender from future and reinsert them with updated height - let all_nonces_from_sender = match self.future.by_address.row(sender) { - Some(row_map) => row_map.keys().cloned().collect::>(), - None => vec![], - }; - for k in all_nonces_from_sender { - let order = self.future.drop(sender, &k).expect("iterating over a collection that has been retrieved above; qed"); - if k >= current_nonce { - self.future.insert(*sender, k, order.update_height(k, current_nonce)); - } else { - trace!(target: "txqueue", "Removing old transaction: {:?} (nonce: {} < {})", order.hash, k, current_nonce); - // Remove the transaction completely - self.by_hash.remove(&order.hash).expect("All transactions in `future` are also in `by_hash`"); - } - } - } - - /// Drop all transactions from given sender from `current`. - /// Either moves them to `future` or removes them from queue completely. - fn move_all_to_future(&mut self, sender: &Address, current_nonce: U256) { - let all_nonces_from_sender = match self.current.by_address.row(sender) { - Some(row_map) => row_map.keys().cloned().collect::>(), - None => vec![], - }; - - for k in all_nonces_from_sender { - // Goes to future or is removed - let order = self.current.drop(sender, &k).expect("iterating over a collection that has been retrieved above; - qed"); - if k >= current_nonce { - let order = order.update_height(k, current_nonce); - if order.origin.is_local() { - self.local_transactions.mark_future(order.hash); - } - if let Some(old) = self.future.insert(*sender, k, order.clone()) { - Self::replace_orders(*sender, k, old, order, &mut self.future, &mut self.by_hash, &mut self.local_transactions); - } - } else { - trace!(target: "txqueue", "Removing old transaction: {:?} (nonce: {} < {})", order.hash, k, current_nonce); - let tx = self.by_hash.remove(&order.hash).expect("All transactions in `future` are also in `by_hash`"); - if tx.origin.is_local() { - self.local_transactions.mark_mined(tx.transaction); - } - } - } - self.future.enforce_limit(&mut self.by_hash, &mut self.local_transactions); - } - - /// Returns top transactions from the queue ordered by priority. - pub fn top_transactions(&self) -> Vec { - self.top_transactions_at(BlockNumber::max_value(), u64::max_value(), None) - - } - - fn filter_pending_transaction(&self, best_block: BlockNumber, best_timestamp: u64, nonce_cap: Option, mut f: F) - where F: FnMut(&VerifiedTransaction) { - - let mut delayed = HashSet::new(); - for t in self.current.by_priority.iter() { - let tx = self.by_hash.get(&t.hash).expect("All transactions in `current` and `future` are always included in `by_hash`"); - let sender = tx.sender(); - if delayed.contains(&sender) { - continue; - } - if let Some(max_nonce) = nonce_cap { - if tx.nonce() >= max_nonce { - continue; - } - } - let delay = match tx.condition { - Some(transaction::Condition::Number(n)) => n > best_block, - Some(transaction::Condition::Timestamp(t)) => t > best_timestamp, - None => false, - }; - if delay { - delayed.insert(sender); - continue; - } - f(&tx); - } - } - - /// Returns top transactions from the queue ordered by priority. - pub fn top_transactions_at(&self, best_block: BlockNumber, best_timestamp: u64, nonce_cap: Option) -> Vec { - let mut r = Vec::new(); - self.filter_pending_transaction(best_block, best_timestamp, nonce_cap, |tx| r.push(tx.transaction.clone())); - r - } - - /// Return all ready transactions. - pub fn pending_transactions(&self, best_block: BlockNumber, best_timestamp: u64) -> Vec { - let mut r = Vec::new(); - self.filter_pending_transaction(best_block, best_timestamp, None, |tx| r.push(PendingTransaction::new(tx.transaction.clone(), tx.condition.clone()))); - r - } - - /// Return all future transactions. - pub fn future_transactions(&self) -> Vec { - self.future.by_priority - .iter() - .map(|t| self.by_hash.get(&t.hash).expect("All transactions in `current` and `future` are always included in `by_hash`")) - .map(|t| PendingTransaction { transaction: t.transaction.clone(), condition: t.condition.clone() }) - .collect() - } - - /// Returns local transactions (some of them might not be part of the queue anymore). - pub fn local_transactions(&self) -> &LinkedHashMap { - self.local_transactions.all_transactions() - } - - /// Returns hashes of all transactions from current, ordered by priority. - pub fn pending_hashes(&self) -> Vec { - self.current.by_priority - .iter() - .map(|t| t.hash) - .collect() - } - - /// Returns true if there is at least one local transaction pending - pub fn has_local_pending_transactions(&self) -> bool { - self.current.by_priority.iter().any(|tx| tx.origin == TransactionOrigin::Local) - } - - /// Finds transaction in the queue by hash (if any) - pub fn find(&self, hash: &H256) -> Option { - self.by_hash.get(hash).map(|tx| PendingTransaction { transaction: tx.transaction.clone(), condition: tx.condition.clone() }) - } - - /// Removes all elements (in any state) from the queue - pub fn clear(&mut self) { - self.current.clear(); - self.future.clear(); - self.by_hash.clear(); - self.last_nonces.clear(); - } - - /// Returns highest transaction nonce for given address. - pub fn last_nonce(&self, address: &Address) -> Option { - self.last_nonces.get(address).cloned() - } - - /// Checks if there are any transactions in `future` that should actually be promoted to `current` - /// (because nonce matches). - fn move_matching_future_to_current(&mut self, address: Address, mut current_nonce: U256, first_nonce: U256) { - let mut update_last_nonce_to = None; - { - let by_nonce = self.future.by_address.row_mut(&address); - if by_nonce.is_none() { - return; - } - let by_nonce = by_nonce.expect("None is tested in early-exit condition above; qed"); - while let Some(order) = by_nonce.remove(¤t_nonce) { - // remove also from priority and gas_price - self.future.by_priority.remove(&order); - self.future.by_gas_price.remove(&order.gas_price, &order.hash); - // Put to current - let order = order.update_height(current_nonce, first_nonce); - if order.origin.is_local() { - self.local_transactions.mark_pending(order.hash); - } - if let Some(old) = self.current.insert(address, current_nonce, order.clone()) { - Self::replace_orders(address, current_nonce, old, order, &mut self.current, &mut self.by_hash, &mut self.local_transactions); - } - update_last_nonce_to = Some(current_nonce); - current_nonce = current_nonce + U256::one(); - } - } - self.future.by_address.clear_if_empty(&address); - if let Some(x) = update_last_nonce_to { - // Update last inserted nonce - self.last_nonces.insert(address, x); - } - } - - /// Adds VerifiedTransaction to this queue. - /// - /// Determines if it should be placed in current or future. When transaction is - /// imported to `current` also checks if there are any `future` transactions that should be promoted because of - /// this. - /// - /// It ignores transactions that has already been imported (same `hash`) and replaces the transaction - /// iff `(address, nonce)` is the same but `gas_price` is higher. - /// - /// Returns `true` when transaction was imported successfuly - fn import_tx(&mut self, tx: VerifiedTransaction, state_nonce: U256) -> Result { - - if self.by_hash.get(&tx.hash()).is_some() { - // Transaction is already imported. - trace!(target: "txqueue", "Dropping already imported transaction: {:?}", tx.hash()); - return Err(transaction::Error::AlreadyImported); - } - - let min_gas_price = (self.minimal_gas_price, self.strategy); - let address = tx.sender(); - let nonce = tx.nonce(); - let hash = tx.hash(); - - // The transaction might be old, let's check that. - // This has to be the first test, otherwise calculating - // nonce height would result in overflow. - if nonce < state_nonce { - // Droping transaction - trace!(target: "txqueue", "Dropping old transaction: {:?} (nonce: {} < {})", tx.hash(), nonce, state_nonce); - return Err(transaction::Error::Old); - } - - // Update nonces of transactions in future (remove old transactions) - self.update_future(&address, state_nonce); - // State nonce could be updated. Maybe there are some more items waiting in future? - self.move_matching_future_to_current(address, state_nonce, state_nonce); - // Check the next expected nonce (might be updated by move above) - let next_nonce = self.last_nonces - .get(&address) - .cloned() - .map_or(state_nonce, |n| n + U256::one()); - - if tx.origin.is_local() { - self.mark_transactions_local(&address); - } - - // Future transaction - if nonce > next_nonce { - // We have a gap - put to future. - // Insert transaction (or replace old one with lower gas price) - check_too_cheap( - Self::replace_transaction(tx, state_nonce, min_gas_price, &mut self.future, &mut self.by_hash, &mut self.local_transactions) - )?; - // Enforce limit in Future - let removed = self.future.enforce_limit(&mut self.by_hash, &mut self.local_transactions); - // Return an error if this transaction was not imported because of limit. - check_if_removed(&address, &nonce, removed)?; - - debug!(target: "txqueue", "Importing transaction to future: {:?}", hash); - debug!(target: "txqueue", "status: {:?}", self.status()); - return Ok(transaction::ImportResult::Future); - } - - // We might have filled a gap - move some more transactions from future - self.move_matching_future_to_current(address, nonce, state_nonce); - self.move_matching_future_to_current(address, nonce + U256::one(), state_nonce); - - // Replace transaction if any - check_too_cheap( - Self::replace_transaction(tx, state_nonce, min_gas_price, &mut self.current, &mut self.by_hash, &mut self.local_transactions) - )?; - // Keep track of highest nonce stored in current - let new_max = self.last_nonces.get(&address).map_or(nonce, |n| cmp::max(nonce, *n)); - self.last_nonces.insert(address, new_max); - - // Also enforce the limit - let removed = self.current.enforce_limit(&mut self.by_hash, &mut self.local_transactions); - // If some transaction were removed because of limit we need to update last_nonces also. - self.update_last_nonces(&removed); - // Trigger error if the transaction we are importing was removed. - check_if_removed(&address, &nonce, removed)?; - - debug!(target: "txqueue", "Imported transaction to current: {:?}", hash); - debug!(target: "txqueue", "status: {:?}", self.status()); - Ok(transaction::ImportResult::Current) - } - - /// Updates - fn update_last_nonces(&mut self, removed_min_nonces: &Option>) { - if let Some(ref min_nonces) = *removed_min_nonces { - for (sender, nonce) in min_nonces.iter() { - if *nonce == U256::zero() { - self.last_nonces.remove(sender); - } else { - self.last_nonces.insert(*sender, *nonce - U256::one()); - } - } - } - } - - /// Replaces transaction in given set (could be `future` or `current`). - /// - /// If there is already transaction with same `(sender, nonce)` it will be replaced iff `gas_price` is higher. - /// One of the transactions is dropped from set and also removed from queue entirely (from `by_hash`). - /// - /// Returns `true` if transaction actually got to the queue (`false` if there was already a transaction with higher - /// gas_price) - fn replace_transaction( - tx: VerifiedTransaction, - base_nonce: U256, - min_gas_price: (U256, PrioritizationStrategy), - set: &mut TransactionSet, - by_hash: &mut HashMap, - local: &mut LocalTransactionsList, - ) -> bool { - let order = TransactionOrder::for_transaction(&tx, base_nonce, min_gas_price.0, min_gas_price.1); - let hash = tx.hash(); - let address = tx.sender(); - let nonce = tx.nonce(); - - let old_hash = by_hash.insert(hash, tx); - assert!(old_hash.is_none(), "Each hash has to be inserted exactly once."); - - trace!(target: "txqueue", "Inserting: {:?}", order); - - if let Some(old) = set.insert(address, nonce, order.clone()) { - Self::replace_orders(address, nonce, old, order, set, by_hash, local) - } else { - true - } - } - - fn replace_orders( - address: Address, - nonce: U256, - old: TransactionOrder, - order: TransactionOrder, - set: &mut TransactionSet, - by_hash: &mut HashMap, - local: &mut LocalTransactionsList, - ) -> bool { - // There was already transaction in queue. Let's check which one should stay - let old_hash = old.hash; - let new_hash = order.hash; - - let old_gas_price = old.gas_price; - let new_gas_price = order.gas_price; - let min_required_gas_price = old_gas_price + (old_gas_price >> GAS_PRICE_BUMP_SHIFT); - - if min_required_gas_price > new_gas_price { - trace!(target: "txqueue", "Didn't insert transaction because gas price was too low: {:?} ({:?} stays in the queue)", order.hash, old.hash); - // Put back old transaction since it has greater priority (higher gas_price) - set.insert(address, nonce, old); - // and remove new one - let order = by_hash.remove(&order.hash).expect("The hash has been just inserted and no other line is altering `by_hash`."); - if order.origin.is_local() { - local.mark_replaced(order.transaction, old_gas_price, old_hash); - } - false - } else { - trace!(target: "txqueue", "Replaced transaction: {:?} with transaction with higher gas price: {:?}", old.hash, order.hash); - // Make sure we remove old transaction entirely - let old = by_hash.remove(&old.hash).expect("The hash is coming from `future` so it has to be in `by_hash`."); - if old.origin.is_local() { - local.mark_replaced(old.transaction, new_gas_price, new_hash); - } - true - } - } -} - -fn check_too_cheap(is_in: bool) -> Result<(), transaction::Error> { - if is_in { - Ok(()) - } else { - Err(transaction::Error::TooCheapToReplace) - } -} - -fn check_if_removed(sender: &Address, nonce: &U256, dropped: Option>) -> Result<(), - transaction::Error> { - match dropped { - Some(ref dropped) => match dropped.get(sender) { - Some(min) if nonce >= min => { - Err(transaction::Error::LimitReached) - }, - _ => Ok(()), - }, - _ => Ok(()), - } -} - - -#[cfg(test)] -pub mod test { - use ethereum_types::{U256, Address}; - use super::*; - use ethkey::{Random, Generator}; - use rustc_hex::FromHex; - use transaction::Transaction; - - pub struct DummyTransactionDetailsProvider { - account_details: AccountDetails, - gas_required: U256, - service_transactions_check_result: Result, - } - - impl Default for DummyTransactionDetailsProvider { - fn default() -> Self { - DummyTransactionDetailsProvider { - account_details: default_account_details(), - gas_required: U256::zero(), - service_transactions_check_result: Ok(false), - } - } - } - - impl DummyTransactionDetailsProvider { - pub fn with_account(mut self, account_details: AccountDetails) -> Self { - self.account_details = account_details; - self - } - - pub fn with_account_nonce(mut self, nonce: U256) -> Self { - self.account_details.nonce = nonce; - self - } - - pub fn with_tx_gas_required(mut self, gas_required: U256) -> Self { - self.gas_required = gas_required; - self - } - - pub fn service_transaction_checker_returns_error(mut self, error: &str) -> Self { - self.service_transactions_check_result = Err(error.to_owned()); - self - } - - pub fn service_transaction_checker_accepts(mut self, accepts: bool) -> Self { - self.service_transactions_check_result = Ok(accepts); - self - } - } - - impl TransactionDetailsProvider for DummyTransactionDetailsProvider { - fn fetch_account(&self, _address: &Address) -> AccountDetails { - AccountDetails { - nonce: self.account_details.nonce, - balance: self.account_details.balance, - } - } - - fn estimate_gas_required(&self, _tx: &SignedTransaction) -> U256 { - self.gas_required - } - - fn is_service_transaction_acceptable(&self, _tx: &SignedTransaction) -> Result { - self.service_transactions_check_result.clone() - } - } - - fn unwrap_tx_err(err: Result) -> transaction::Error { - err.unwrap_err() - } - - fn default_nonce() -> U256 { 123.into() } - fn default_gas_val() -> U256 { 100_000.into() } - fn default_gas_price() -> U256 { 1.into() } - - fn new_unsigned_tx(nonce: U256, gas: U256, gas_price: U256) -> Transaction { - Transaction { - action: transaction::Action::Create, - value: U256::from(100), - data: "3331600055".from_hex().unwrap(), - gas: gas, - gas_price: gas_price, - nonce: nonce - } - } - - fn new_tx(nonce: U256, gas_price: U256) -> SignedTransaction { - let keypair = Random.generate().unwrap(); - new_unsigned_tx(nonce, default_gas_val(), gas_price).sign(keypair.secret(), None) - } - - fn new_tx_with_gas(gas: U256, gas_price: U256) -> SignedTransaction { - let keypair = Random.generate().unwrap(); - new_unsigned_tx(default_nonce(), gas, gas_price).sign(keypair.secret(), None) - } - - fn new_tx_default() -> SignedTransaction { - new_tx(default_nonce(), default_gas_price()) - } - - fn default_account_details() -> AccountDetails { - AccountDetails { - nonce: default_nonce(), - balance: !U256::zero() - } - } - - fn default_account_details_for_addr(_a: &Address) -> AccountDetails { - default_account_details() - } - - fn default_tx_provider() -> DummyTransactionDetailsProvider { - DummyTransactionDetailsProvider::default() - } - - fn new_tx_pair(nonce: U256, gas_price: U256, nonce_increment: U256, gas_price_increment: U256) -> (SignedTransaction, SignedTransaction) { - let tx1 = new_unsigned_tx(nonce, default_gas_val(), gas_price); - let tx2 = new_unsigned_tx(nonce + nonce_increment, default_gas_val(), gas_price + gas_price_increment); - - let keypair = Random.generate().unwrap(); - let secret = &keypair.secret(); - (tx1.sign(secret, None).into(), tx2.sign(secret, None).into()) - } - - /// Returns two consecutive transactions, both with increased gas price - fn new_tx_pair_with_gas_price_increment(gas_price_increment: U256) -> (SignedTransaction, SignedTransaction) { - let gas = default_gas_price() + gas_price_increment; - let tx1 = new_unsigned_tx(default_nonce(), default_gas_val(), gas); - let tx2 = new_unsigned_tx(default_nonce() + 1.into(), default_gas_val(), gas); - - let keypair = Random.generate().unwrap(); - let secret = &keypair.secret(); - (tx1.sign(secret, None).into(), tx2.sign(secret, None).into()) - } - - fn new_tx_pair_default(nonce_increment: U256, gas_price_increment: U256) -> (SignedTransaction, SignedTransaction) { - new_tx_pair(default_nonce(), default_gas_price(), nonce_increment, gas_price_increment) - } - - /// Returns two transactions with identical (sender, nonce) but different gas price/hash. - fn new_similar_tx_pair() -> (SignedTransaction, SignedTransaction) { - new_tx_pair_default(0.into(), 1.into()) - } - - #[test] - fn test_ordering() { - assert_eq!(TransactionOrigin::Local.cmp(&TransactionOrigin::External), Ordering::Less); - assert_eq!(TransactionOrigin::RetractedBlock.cmp(&TransactionOrigin::Local), Ordering::Less); - assert_eq!(TransactionOrigin::RetractedBlock.cmp(&TransactionOrigin::External), Ordering::Less); - - assert_eq!(TransactionOrigin::External.cmp(&TransactionOrigin::Local), Ordering::Greater); - assert_eq!(TransactionOrigin::Local.cmp(&TransactionOrigin::RetractedBlock), Ordering::Greater); - assert_eq!(TransactionOrigin::External.cmp(&TransactionOrigin::RetractedBlock), Ordering::Greater); - } - - fn transaction_order(tx: &VerifiedTransaction, nonce: U256) -> TransactionOrder { - TransactionOrder::for_transaction(tx, nonce, 0.into(), PrioritizationStrategy::GasPriceOnly) - } - - #[test] - fn should_return_correct_nonces_when_dropped_because_of_limit() { - // given - let mut txq = TransactionQueue::with_limits( - PrioritizationStrategy::GasPriceOnly, - 2, - usize::max_value(), - !U256::zero(), - !U256::zero(), - ); - let (tx1, tx2) = new_tx_pair(123.into(), 1.into(), 1.into(), 0.into()); - let sender = tx1.sender(); - let nonce = tx1.nonce; - txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - assert_eq!(txq.status().pending, 2); - assert_eq!(txq.last_nonce(&sender), Some(nonce + 1.into())); - - // when - let tx = new_tx(123.into(), 1.into()); - let res = txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()); - - // then - // No longer the case as we don't even consider a transaction that isn't above a full - // queue's minimum gas price. - // We may want to reconsider this in the near future so leaving this code in as a - // possible alternative. - /* - assert_eq!(res.unwrap(), transaction::ImportResult::Current); - assert_eq!(txq.status().pending, 2); - assert_eq!(txq.last_nonce(&sender), Some(nonce)); - */ - assert_eq!(unwrap_tx_err(res), transaction::Error::InsufficientGasPrice { - minimal: 2.into(), - got: 1.into(), - }); - assert_eq!(txq.status().pending, 2); - assert_eq!(txq.last_nonce(&sender), Some(tx2.nonce)); - } - - #[test] - fn should_create_transaction_set() { - // given - let mut local = LocalTransactionsList::default(); - let mut set = TransactionSet { - by_priority: BTreeSet::new(), - by_address: Table::new(), - by_gas_price: Default::default(), - limit: 1, - total_gas_limit: !U256::zero(), - memory_limit: usize::max_value(), - }; - let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); - let tx1 = VerifiedTransaction::new(tx1, TransactionOrigin::External, None, 0, 0); - let tx2 = VerifiedTransaction::new(tx2, TransactionOrigin::External, None, 0, 1); - let mut by_hash = { - let mut x = HashMap::new(); - let tx1 = VerifiedTransaction::new(tx1.transaction.clone(), TransactionOrigin::External, None, 0, 0); - let tx2 = VerifiedTransaction::new(tx2.transaction.clone(), TransactionOrigin::External, None, 0, 1); - x.insert(tx1.hash(), tx1); - x.insert(tx2.hash(), tx2); - x - }; - // Insert both transactions - let order1 = transaction_order(&tx1, U256::zero()); - set.insert(tx1.sender(), tx1.nonce(), order1.clone()); - let order2 = transaction_order(&tx2, U256::zero()); - set.insert(tx2.sender(), tx2.nonce(), order2.clone()); - assert_eq!(set.by_priority.len(), 2); - assert_eq!(set.by_address.len(), 2); - - // when - set.enforce_limit(&mut by_hash, &mut local); - - // then - assert_eq!(by_hash.len(), 1); - assert_eq!(set.by_priority.len(), 1); - assert_eq!(set.by_address.len(), 1); - assert_eq!(set.by_priority.iter().next().unwrap().clone(), order1); - set.clear(); - assert_eq!(set.by_priority.len(), 0); - assert_eq!(set.by_address.len(), 0); - } - - #[test] - fn should_replace_transaction_in_set() { - let mut set = TransactionSet { - by_priority: BTreeSet::new(), - by_address: Table::new(), - by_gas_price: Default::default(), - limit: 1, - total_gas_limit: !U256::zero(), - memory_limit: 0, - }; - // Create two transactions with same nonce - // (same hash) - let (tx1, tx2) = new_tx_pair_default(0.into(), 0.into()); - let tx1 = VerifiedTransaction::new(tx1, TransactionOrigin::External, None, 0, 0); - let tx2 = VerifiedTransaction::new(tx2, TransactionOrigin::External, None, 0, 1); - let by_hash = { - let mut x = HashMap::new(); - let tx1 = VerifiedTransaction::new(tx1.transaction.clone(), TransactionOrigin::External, None, 0, 0); - let tx2 = VerifiedTransaction::new(tx2.transaction.clone(), TransactionOrigin::External, None, 0, 1); - x.insert(tx1.hash(), tx1); - x.insert(tx2.hash(), tx2); - x - }; - // Insert both transactions - let order1 = transaction_order(&tx1, U256::zero()); - set.insert(tx1.sender(), tx1.nonce(), order1.clone()); - assert_eq!(set.by_priority.len(), 1); - assert_eq!(set.by_address.len(), 1); - assert_eq!(set.by_gas_price.len(), 1); - assert_eq!(*set.by_gas_price.iter().next().unwrap().0, 1.into()); - assert_eq!(set.by_gas_price.iter().next().unwrap().1.len(), 1); - // Two different orders (imagine nonce changed in the meantime) - let order2 = transaction_order(&tx2, U256::one()); - set.insert(tx2.sender(), tx2.nonce(), order2.clone()); - assert_eq!(set.by_priority.len(), 1); - assert_eq!(set.by_address.len(), 1); - assert_eq!(set.by_gas_price.len(), 1); - assert_eq!(*set.by_gas_price.iter().next().unwrap().0, 1.into()); - assert_eq!(set.by_gas_price.iter().next().unwrap().1.len(), 1); - - // then - assert_eq!(by_hash.len(), 1); - assert_eq!(set.by_priority.len(), 1); - assert_eq!(set.by_address.len(), 1); - assert_eq!(set.by_gas_price.len(), 1); - assert_eq!(*set.by_gas_price.iter().next().unwrap().0, 1.into()); - assert_eq!(set.by_gas_price.iter().next().unwrap().1.len(), 1); - assert_eq!(set.by_priority.iter().next().unwrap().clone(), order2); - } - - #[test] - fn should_not_insert_same_transaction_twice_into_set() { - let mut set = TransactionSet { - by_priority: BTreeSet::new(), - by_address: Table::new(), - by_gas_price: Default::default(), - limit: 2, - total_gas_limit: !U256::zero(), - memory_limit: 0, - }; - let tx = new_tx_default(); - let tx1 = VerifiedTransaction::new(tx.clone(), TransactionOrigin::External, None, 0, 0); - let order1 = TransactionOrder::for_transaction(&tx1, 0.into(), 1.into(), PrioritizationStrategy::GasPriceOnly); - assert!(set.insert(tx1.sender(), tx1.nonce(), order1).is_none()); - let tx2 = VerifiedTransaction::new(tx, TransactionOrigin::External, None, 0, 1); - let order2 = TransactionOrder::for_transaction(&tx2, 0.into(), 1.into(), PrioritizationStrategy::GasPriceOnly); - assert!(set.insert(tx2.sender(), tx2.nonce(), order2).is_some()); - } - - #[test] - fn should_give_correct_gas_price_entry_limit() { - let mut set = TransactionSet { - by_priority: BTreeSet::new(), - by_address: Table::new(), - by_gas_price: Default::default(), - limit: 1, - total_gas_limit: !U256::zero(), - memory_limit: 0, - }; - - assert_eq!(set.gas_price_entry_limit(), 0.into()); - let tx = new_tx_default(); - let tx1 = VerifiedTransaction::new(tx.clone(), TransactionOrigin::External, None, 0, 0); - let order1 = TransactionOrder::for_transaction(&tx1, 0.into(), 1.into(), PrioritizationStrategy::GasPriceOnly); - assert!(set.insert(tx1.sender(), tx1.nonce(), order1.clone()).is_none()); - assert_eq!(set.gas_price_entry_limit(), 2.into()); - } - - #[test] - fn should_handle_same_transaction_imported_twice_with_different_state_nonces() { - // given - let mut txq = TransactionQueue::default(); - let (tx, tx2) = new_similar_tx_pair(); - let prev_nonce = default_account_details().nonce - U256::one(); - - // First insert one transaction to future - let res = txq.add(tx, TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(prev_nonce)); - assert_eq!(res.unwrap(), transaction::ImportResult::Future); - assert_eq!(txq.status().future, 1); - - // now import second transaction to current - let res = txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()); - - // and then there should be only one transaction in current (the one with higher gas_price) - assert_eq!(res.unwrap(), transaction::ImportResult::Current); - assert_eq!(txq.status().pending, 1); - assert_eq!(txq.status().future, 0); - assert_eq!(txq.current.by_priority.len(), 1); - assert_eq!(txq.current.by_address.len(), 1); - let top = txq.top_transactions(); - assert_eq!(top[0], tx2); - } - - #[test] - fn should_move_all_transactions_from_future() { - // given - let mut txq = TransactionQueue::default(); - let (tx, tx2) = new_tx_pair_default(1.into(), 1.into()); - let prev_nonce = default_account_details().nonce - U256::one(); - - // First insert one transaction to future - let res = txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(prev_nonce)); - assert_eq!(res.unwrap(), transaction::ImportResult::Future); - assert_eq!(txq.status().future, 1); - - // now import second transaction to current - let res = txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()); - - // then - assert_eq!(res.unwrap(), transaction::ImportResult::Current); - assert_eq!(txq.status().pending, 2); - assert_eq!(txq.status().future, 0); - assert_eq!(txq.current.by_priority.len(), 2); - assert_eq!(txq.current.by_address.len(), 2); - let top = txq.top_transactions(); - assert_eq!(top[0], tx); - assert_eq!(top[1], tx2); - } - - #[test] - fn should_import_tx() { - // given - let mut txq = TransactionQueue::default(); - let tx = new_tx_default(); - - // when - let res = txq.add(tx, TransactionOrigin::External, 0, None, &default_tx_provider()); - - // then - assert_eq!(res.unwrap(), transaction::ImportResult::Current); - let stats = txq.status(); - assert_eq!(stats.pending, 1); - } - - #[test] - fn should_order_by_gas() { - // given - let mut txq = TransactionQueue::new(PrioritizationStrategy::GasAndGasPrice); - let tx1 = new_tx_with_gas(50000.into(), 40.into()); - let tx2 = new_tx_with_gas(40000.into(), 30.into()); - let tx3 = new_tx_with_gas(30000.into(), 10.into()); - let tx4 = new_tx_with_gas(50000.into(), 20.into()); - txq.set_minimal_gas_price(15.into()); - - // when - let res1 = txq.add(tx1, TransactionOrigin::External, 0, None, &default_tx_provider()); - let res2 = txq.add(tx2, TransactionOrigin::External, 0, None, &default_tx_provider()); - let res3 = txq.add(tx3, TransactionOrigin::External, 0, None, &default_tx_provider()); - let res4 = txq.add(tx4, TransactionOrigin::External, 0, None, &default_tx_provider()); - - // then - assert_eq!(res1.unwrap(), transaction::ImportResult::Current); - assert_eq!(res2.unwrap(), transaction::ImportResult::Current); - assert_eq!(unwrap_tx_err(res3), transaction::Error::InsufficientGasPrice { - minimal: U256::from(15), - got: U256::from(10), - }); - assert_eq!(res4.unwrap(), transaction::ImportResult::Current); - let stats = txq.status(); - assert_eq!(stats.pending, 3); - assert_eq!(txq.top_transactions()[0].gas, 40000.into()); - assert_eq!(txq.top_transactions()[1].gas, 50000.into()); - assert_eq!(txq.top_transactions()[2].gas, 50000.into()); - assert_eq!(txq.top_transactions()[1].gas_price, 40.into()); - assert_eq!(txq.top_transactions()[2].gas_price, 20.into()); - } - - #[test] - fn should_order_by_gas_factor() { - // given - let mut txq = TransactionQueue::new(PrioritizationStrategy::GasFactorAndGasPrice); - - let tx1 = new_tx_with_gas(150_000.into(), 40.into()); - let tx2 = new_tx_with_gas(40_000.into(), 16.into()); - let tx3 = new_tx_with_gas(30_000.into(), 15.into()); - let tx4 = new_tx_with_gas(150_000.into(), 62.into()); - txq.set_minimal_gas_price(15.into()); - - // when - let res1 = txq.add(tx1, TransactionOrigin::External, 0, None, &default_tx_provider()); - let res2 = txq.add(tx2, TransactionOrigin::External, 0, None, &default_tx_provider()); - let res3 = txq.add(tx3, TransactionOrigin::External, 0, None, &default_tx_provider()); - let res4 = txq.add(tx4, TransactionOrigin::External, 0, None, &default_tx_provider()); - - // then - assert_eq!(res1.unwrap(), transaction::ImportResult::Current); - assert_eq!(res2.unwrap(), transaction::ImportResult::Current); - assert_eq!(res3.unwrap(), transaction::ImportResult::Current); - assert_eq!(res4.unwrap(), transaction::ImportResult::Current); - let stats = txq.status(); - assert_eq!(stats.pending, 4); - assert_eq!(txq.top_transactions()[0].gas, 30_000.into()); - assert_eq!(txq.top_transactions()[1].gas, 150_000.into()); - assert_eq!(txq.top_transactions()[2].gas, 40_000.into()); - assert_eq!(txq.top_transactions()[3].gas, 150_000.into()); - assert_eq!(txq.top_transactions()[0].gas_price, 15.into()); - assert_eq!(txq.top_transactions()[1].gas_price, 62.into()); - assert_eq!(txq.top_transactions()[2].gas_price, 16.into()); - assert_eq!(txq.top_transactions()[3].gas_price, 40.into()); - } - - #[test] - fn tx_gas_limit_should_never_overflow() { - // given - let mut txq = TransactionQueue::default(); - txq.set_gas_limit(U256::zero()); - assert_eq!(txq.block_gas_limit, U256::zero()); - - // when - txq.set_gas_limit(!U256::zero()); - - // then - assert_eq!(txq.block_gas_limit, !U256::zero()); - } - - #[test] - fn should_not_import_transaction_above_gas_limit() { - // given - let mut txq = TransactionQueue::default(); - let tx = new_tx_default(); - let gas = tx.gas; - let limit = gas / U256::from(2); - txq.set_gas_limit(limit); - - // when - let res = txq.add(tx, TransactionOrigin::External, 0, None, &default_tx_provider()); - - // then - assert_eq!(unwrap_tx_err(res), transaction::Error::GasLimitExceeded { - limit: U256::from(50_000), - got: gas, - }); - let stats = txq.status(); - assert_eq!(stats.pending, 0); - assert_eq!(stats.future, 0); - } - - - #[test] - fn should_drop_transactions_from_senders_without_balance() { - // given - let mut txq = TransactionQueue::default(); - let tx = new_tx_default(); - let account = AccountDetails { - nonce: default_account_details().nonce, - balance: U256::one() - }; - - // when - let res = txq.add(tx, TransactionOrigin::External, 0, None, &default_tx_provider().with_account(account)); - - // then - assert_eq!(unwrap_tx_err(res), transaction::Error::InsufficientBalance { - balance: U256::from(1), - cost: U256::from(100_100), - }); - let stats = txq.status(); - assert_eq!(stats.pending, 0); - assert_eq!(stats.future, 0); - } - - #[test] - fn should_not_import_transaction_below_min_gas_price_threshold_if_external() { - // given - let mut txq = TransactionQueue::default(); - let tx = new_tx_default(); - txq.set_minimal_gas_price(tx.gas_price + U256::one()); - - // when - let res = txq.add(tx, TransactionOrigin::External, 0, None, &default_tx_provider()); - - // then - assert_eq!(unwrap_tx_err(res), transaction::Error::InsufficientGasPrice { - minimal: U256::from(2), - got: U256::from(1), - }); - let stats = txq.status(); - assert_eq!(stats.pending, 0); - assert_eq!(stats.future, 0); - } - - #[test] - fn should_import_transaction_below_min_gas_price_threshold_if_local() { - // given - let mut txq = TransactionQueue::default(); - let tx = new_tx_default(); - txq.set_minimal_gas_price(tx.gas_price + U256::one()); - - // when - let res = txq.add(tx, TransactionOrigin::Local, 0, None, &default_tx_provider()); - - // then - assert_eq!(res.unwrap(), transaction::ImportResult::Current); - let stats = txq.status(); - assert_eq!(stats.pending, 1); - assert_eq!(stats.future, 0); - } - - #[test] - fn should_import_txs_from_same_sender() { - // given - let mut txq = TransactionQueue::default(); - - let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); - - // when - txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - - // then - let top = txq.top_transactions(); - assert_eq!(top[0], tx); - assert_eq!(top[1], tx2); - assert_eq!(top.len(), 2); - } - - #[test] - fn should_prioritize_local_transactions_within_same_nonce_height() { - // given - let mut txq = TransactionQueue::default(); - let tx = new_tx_default(); - // the second one has same nonce but higher `gas_price` - let (_, tx2) = new_similar_tx_pair(); - - // when - // first insert the one with higher gas price - txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - // then the one with lower gas price, but local - txq.add(tx.clone(), TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap(); - - // then - let top = txq.top_transactions(); - assert_eq!(top[0], tx); // local should be first - assert_eq!(top[1], tx2); - assert_eq!(top.len(), 2); - } - - #[test] - fn when_importing_local_should_mark_others_from_the_same_sender_as_local() { - // given - let mut txq = TransactionQueue::default(); - let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); - // the second one has same nonce but higher `gas_price` - let (_, tx0) = new_similar_tx_pair(); - - txq.add(tx0.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - // the one with higher gas price is first - let top = txq.top_transactions(); - assert_eq!(top[0], tx0); - assert_eq!(top[1], tx1); - - // when - // insert second as local - txq.add(tx2.clone(), TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap(); - - // then - // the order should be updated - let top = txq.top_transactions(); - assert_eq!(top[0], tx1); - assert_eq!(top[1], tx2); - assert_eq!(top[2], tx0); - } - - #[test] - fn should_prioritize_reimported_transactions_within_same_nonce_height() { - // given - let mut txq = TransactionQueue::default(); - let tx = new_tx_default(); - // the second one has same nonce but higher `gas_price` - let (_, tx2) = new_similar_tx_pair(); - - // when - // first insert local one with higher gas price - txq.add(tx2.clone(), TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap(); - // then the one with lower gas price, but from retracted block - txq.add(tx.clone(), TransactionOrigin::RetractedBlock, 0, None, &default_tx_provider()).unwrap(); - - // then - let top = txq.top_transactions(); - assert_eq!(top[0], tx); // retracted should be first - assert_eq!(top[1], tx2); - assert_eq!(top.len(), 2); - } - - #[test] - fn should_not_prioritize_local_transactions_with_different_nonce_height() { - // given - let mut txq = TransactionQueue::default(); - let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); - - // when - txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - txq.add(tx2.clone(), TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap(); - - // then - let top = txq.top_transactions(); - assert_eq!(top[0], tx); - assert_eq!(top[1], tx2); - assert_eq!(top.len(), 2); - } - - #[test] - fn should_penalize_transactions_from_sender_in_future() { - // given - let prev_nonce = default_account_details().nonce - U256::one(); - let mut txq = TransactionQueue::default(); - // txa, txb - slightly bigger gas price to have consistent ordering - let (txa, txb) = new_tx_pair_default(1.into(), 0.into()); - let (tx1, tx2) = new_tx_pair_with_gas_price_increment(3.into()); - - // insert everything - txq.add(txa.clone(), TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(prev_nonce)).unwrap(); - txq.add(txb.clone(), TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(prev_nonce)).unwrap(); - txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(prev_nonce)).unwrap(); - txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(prev_nonce)).unwrap(); - - assert_eq!(txq.status().future, 4); - - // when - txq.penalize(&tx1.hash()); - - // then - let top: Vec<_> = txq.future_transactions().into_iter().map(|tx| tx.transaction).collect(); - assert_eq!(top[0], txa); - assert_eq!(top[1], txb); - assert_eq!(top[2], tx1); - assert_eq!(top[3], tx2); - assert_eq!(top.len(), 4); - } - - #[test] - fn should_not_penalize_local_transactions() { - // given - let mut txq = TransactionQueue::default(); - // txa, txb - slightly bigger gas price to have consistent ordering - let (txa, txb) = new_tx_pair_default(1.into(), 0.into()); - let (tx1, tx2) = new_tx_pair_with_gas_price_increment(3.into()); - - // insert everything - txq.add(txa.clone(), TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap(); - txq.add(txb.clone(), TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap(); - txq.add(tx1.clone(), TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap(); - txq.add(tx2.clone(), TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap(); - - let top = txq.top_transactions(); - assert_eq!(top[0], tx1); - assert_eq!(top[1], txa); - assert_eq!(top[2], tx2); - assert_eq!(top[3], txb); - assert_eq!(top.len(), 4); - - // when - txq.penalize(&tx1.hash()); - - // then (order is the same) - let top = txq.top_transactions(); - assert_eq!(top[0], tx1); - assert_eq!(top[1], txa); - assert_eq!(top[2], tx2); - assert_eq!(top[3], txb); - assert_eq!(top.len(), 4); - } - - #[test] - fn should_penalize_transactions_from_sender() { - // given - let mut txq = TransactionQueue::default(); - // txa, txb - slightly bigger gas price to have consistent ordering - let (txa, txb) = new_tx_pair_default(1.into(), 0.into()); - let (tx1, tx2) = new_tx_pair_with_gas_price_increment(3.into()); - - // insert everything - txq.add(txa.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - txq.add(txb.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - - let top = txq.top_transactions(); - assert_eq!(top[0], tx1); - assert_eq!(top[1], txa); - assert_eq!(top[2], tx2); - assert_eq!(top[3], txb); - assert_eq!(top.len(), 4); - - // when - txq.penalize(&tx1.hash()); - - // then - let top = txq.top_transactions(); - assert_eq!(top[0], txa); - assert_eq!(top[1], txb); - assert_eq!(top[2], tx1); - assert_eq!(top[3], tx2); - assert_eq!(top.len(), 4); - } - - #[test] - fn should_return_pending_hashes() { - // given - let mut txq = TransactionQueue::default(); - - let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); - - // when - txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - - // then - let top = txq.pending_hashes(); - assert_eq!(top[0], tx.hash()); - assert_eq!(top[1], tx2.hash()); - assert_eq!(top.len(), 2); - } - - #[test] - fn should_put_transaction_to_futures_if_gap_detected() { - // given - let mut txq = TransactionQueue::default(); - - let (tx, tx2) = new_tx_pair_default(2.into(), 0.into()); - - // when - let res1 = txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - let res2 = txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - - // then - assert_eq!(res1, transaction::ImportResult::Current); - assert_eq!(res2, transaction::ImportResult::Future); - let stats = txq.status(); - assert_eq!(stats.pending, 1); - assert_eq!(stats.future, 1); - let top = txq.top_transactions(); - assert_eq!(top.len(), 1); - assert_eq!(top[0], tx); - } - - #[test] - fn should_handle_min_block() { - // given - let mut txq = TransactionQueue::default(); - - let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); - - // when - let res1 = txq.add(tx.clone(), TransactionOrigin::External, 0, Some(transaction::Condition::Number(1)), &default_tx_provider()).unwrap(); - let res2 = txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - - // then - assert_eq!(res1, transaction::ImportResult::Current); - assert_eq!(res2, transaction::ImportResult::Current); - let top = txq.top_transactions_at(0, 0, None); - assert_eq!(top.len(), 0); - let top = txq.top_transactions_at(1, 0, None); - assert_eq!(top.len(), 2); - } - - #[test] - fn should_correctly_update_futures_when_removing() { - // given - let prev_nonce = default_account_details().nonce - U256::one(); - let next2_nonce = default_nonce() + U256::from(3); - - let mut txq = TransactionQueue::default(); - - let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); - txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(prev_nonce)).unwrap(); - txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(prev_nonce)).unwrap(); - assert_eq!(txq.status().future, 2); - - // when - txq.cull(tx.sender(), next2_nonce); - // should remove both transactions since they are not valid - - // then - assert_eq!(txq.status().pending, 0); - assert_eq!(txq.status().future, 0); - } - - #[test] - fn should_move_transactions_if_gap_filled() { - // given - let mut txq = TransactionQueue::default(); - let kp = Random.generate().unwrap(); - let secret = kp.secret(); - let tx = new_unsigned_tx(123.into(), default_gas_val(), 1.into()).sign(secret, None).into(); - let tx1 = new_unsigned_tx(124.into(), default_gas_val(), 1.into()).sign(secret, None).into(); - let tx2 = new_unsigned_tx(125.into(), default_gas_val(), 1.into()).sign(secret, None).into(); - - txq.add(tx, TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - assert_eq!(txq.status().pending, 1); - txq.add(tx2, TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - assert_eq!(txq.status().future, 1); - - // when - txq.add(tx1, TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - - // then - let stats = txq.status(); - assert_eq!(stats.pending, 3); - assert_eq!(stats.future, 0); - assert_eq!(txq.future.by_priority.len(), 0); - assert_eq!(txq.future.by_address.len(), 0); - assert_eq!(txq.future.by_gas_price.len(), 0); - } - - #[test] - fn should_remove_transaction() { - // given - let mut txq2 = TransactionQueue::default(); - let (tx, tx2) = new_tx_pair_default(3.into(), 0.into()); - txq2.add(tx.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - txq2.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - assert_eq!(txq2.status().pending, 1); - assert_eq!(txq2.status().future, 1); - - // when - txq2.cull(tx.sender(), tx.nonce + U256::one()); - txq2.cull(tx2.sender(), tx2.nonce + U256::one()); - - // then - let stats = txq2.status(); - assert_eq!(stats.pending, 0); - assert_eq!(stats.future, 0); - } - - #[test] - fn should_move_transactions_to_future_if_gap_introduced() { - // given - let mut txq = TransactionQueue::default(); - let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); - let tx3 = new_tx_default(); - txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - assert_eq!(txq.status().future, 1); - txq.add(tx3.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - assert_eq!(txq.status().pending, 3); - - // when - txq.remove(&tx.hash(), &|_| default_nonce(), RemovalReason::Invalid); - - // then - let stats = txq.status(); - assert_eq!(stats.future, 1); - assert_eq!(stats.pending, 1); - } - - #[test] - fn should_clear_queue() { - // given - let mut txq = TransactionQueue::default(); - let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); - - // add - txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - let stats = txq.status(); - assert_eq!(stats.pending, 2); - - // when - txq.clear(); - - // then - let stats = txq.status(); - assert_eq!(stats.pending, 0); - } - - #[test] - fn should_drop_old_transactions_when_hitting_the_limit() { - // given - let mut txq = TransactionQueue::with_limits( - PrioritizationStrategy::GasPriceOnly, - 1, - usize::max_value(), - !U256::zero(), - !U256::zero() - ); - let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); - let sender = tx.sender(); - let nonce = tx.nonce; - txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - assert_eq!(txq.status().pending, 1); - - // when - let res = txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()); - - // then - let t = txq.top_transactions(); - assert_eq!(unwrap_tx_err(res), transaction::Error::InsufficientGasPrice { minimal: 2.into(), got: 1.into() }); - assert_eq!(txq.status().pending, 1); - assert_eq!(t.len(), 1); - assert_eq!(t[0], tx); - assert_eq!(txq.last_nonce(&sender), Some(nonce)); - } - - #[test] - fn should_limit_future_transactions() { - let mut txq = TransactionQueue::with_limits( - PrioritizationStrategy::GasPriceOnly, - 1, - usize::max_value(), - !U256::zero(), - !U256::zero(), - ); - txq.current.set_limit(10); - let (tx1, tx2) = new_tx_pair_default(4.into(), 1.into()); - let (tx3, tx4) = new_tx_pair_default(4.into(), 2.into()); - txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - txq.add(tx3.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - assert_eq!(txq.status().pending, 2); - - // when - txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - assert_eq!(txq.status().future, 1); - txq.add(tx4.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - - // then - assert_eq!(txq.status().future, 1); - } - - #[test] - fn should_limit_by_gas() { - let mut txq = TransactionQueue::with_limits( - PrioritizationStrategy::GasPriceOnly, - 100, - usize::max_value(), - default_gas_val() * U256::from(2), - !U256::zero() - ); - let (tx1, tx2) = new_tx_pair_default(U256::from(1), U256::from(1)); - let (tx3, tx4) = new_tx_pair_default(U256::from(1), U256::from(2)); - txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - txq.add(tx3.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - // limited by gas - txq.add(tx4.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap_err(); - assert_eq!(txq.status().pending, 2); - } - - #[test] - fn should_keep_own_transactions_above_gas_limit() { - let mut txq = TransactionQueue::with_limits( - PrioritizationStrategy::GasPriceOnly, - 100, - usize::max_value(), - default_gas_val() * U256::from(2), - !U256::zero() - ); - let (tx1, tx2) = new_tx_pair_default(U256::from(1), U256::from(1)); - let (tx3, tx4) = new_tx_pair_default(U256::from(1), U256::from(2)); - let (tx5, _) = new_tx_pair_default(U256::from(1), U256::from(2)); - txq.add(tx1.clone(), TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap(); - txq.add(tx2.clone(), TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap(); - // Not accepted because of limit - txq.add(tx5.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap_err(); - txq.add(tx3.clone(), TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap(); - txq.add(tx4.clone(), TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap(); - assert_eq!(txq.status().pending, 4); - } - - #[test] - fn should_drop_transactions_with_old_nonces() { - let mut txq = TransactionQueue::default(); - let tx = new_tx_default(); - let last_nonce = tx.nonce + U256::one(); - - // when - let res = txq.add(tx, TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(last_nonce)); - - // then - assert_eq!(unwrap_tx_err(res), transaction::Error::Old); - let stats = txq.status(); - assert_eq!(stats.pending, 0); - assert_eq!(stats.future, 0); - } - - #[test] - fn should_not_insert_same_transaction_twice() { - // given - let nonce = default_account_details().nonce + U256::one(); - let mut txq = TransactionQueue::default(); - let (_tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); - txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - assert_eq!(txq.status().future, 1); - assert_eq!(txq.status().pending, 0); - - // when - let res = txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(nonce)); - - // then - assert_eq!(unwrap_tx_err(res), transaction::Error::AlreadyImported); - let stats = txq.status(); - assert_eq!(stats.future, 1); - assert_eq!(stats.pending, 0); - } - - #[test] - fn should_accept_same_transaction_twice_if_removed() { - // given - let mut txq = TransactionQueue::default(); - let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); - txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - assert_eq!(txq.status().pending, 2); - - // when - txq.remove(&tx1.hash(), &|_| default_nonce(), RemovalReason::Invalid); - assert_eq!(txq.status().pending, 0); - assert_eq!(txq.status().future, 1); - txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - - // then - let stats = txq.status(); - assert_eq!(stats.future, 0); - assert_eq!(stats.pending, 2); - } - - #[test] - fn should_not_move_to_future_if_state_nonce_is_higher() { - // given - let mut txq = TransactionQueue::default(); - let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); - let tx3 = new_tx_default(); - txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - assert_eq!(txq.status().future, 1); - txq.add(tx3.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - assert_eq!(txq.status().pending, 3); - - // when - txq.cull(tx.sender(), default_nonce() + U256::one()); - - // then - let stats = txq.status(); - assert_eq!(stats.future, 0); - assert_eq!(stats.pending, 2); - } - - #[test] - fn should_not_replace_same_transaction_if_the_fee_is_less_than_minimal_bump() { - // given - let mut txq = TransactionQueue::default(); - let keypair = Random.generate().unwrap(); - let tx = new_unsigned_tx(123.into(), default_gas_val(), 20.into()).sign(keypair.secret(), None); - let tx2 = { - let mut tx2 = (**tx).clone(); - tx2.gas_price = U256::from(21); - tx2.sign(keypair.secret(), None) - }; - - // when - txq.add(tx, TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - let res = txq.add(tx2, TransactionOrigin::External, 0, None, &default_tx_provider()); - - // then - assert_eq!(unwrap_tx_err(res), transaction::Error::TooCheapToReplace); - let stats = txq.status(); - assert_eq!(stats.pending, 1); - assert_eq!(stats.future, 0); - assert_eq!(txq.top_transactions()[0].gas_price, U256::from(20)); - } - - #[test] - fn should_replace_same_transaction_when_has_higher_fee() { - // given - let mut txq = TransactionQueue::default(); - let keypair = Random.generate().unwrap(); - let tx = new_unsigned_tx(123.into(), default_gas_val(), 10.into()).sign(keypair.secret(), None); - let tx2 = { - let mut tx2 = (**tx).clone(); - tx2.gas_price = U256::from(20); - tx2.sign(keypair.secret(), None) - }; - - // when - txq.add(tx, TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - txq.add(tx2, TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - - // then - let stats = txq.status(); - assert_eq!(stats.pending, 1); - assert_eq!(stats.future, 0); - assert_eq!(txq.top_transactions()[0].gas_price, U256::from(20)); - } - - #[test] - fn should_replace_same_transaction_when_importing_to_futures() { - // given - let mut txq = TransactionQueue::default(); - let keypair = Random.generate().unwrap(); - let tx0 = new_unsigned_tx(123.into(), default_gas_val(), 1.into()).sign(keypair.secret(), None); - let tx1 = { - let mut tx1 = (**tx0).clone(); - tx1.nonce = U256::from(124); - tx1.sign(keypair.secret(), None) - }; - let tx2 = { - let mut tx2 = (**tx1).clone(); - tx2.gas_price = U256::from(200); - tx2.sign(keypair.secret(), None) - }; - - // when - txq.add(tx1, TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - txq.add(tx2, TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - assert_eq!(txq.status().future, 1); - txq.add(tx0, TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - - // then - let stats = txq.status(); - assert_eq!(stats.future, 0); - assert_eq!(stats.pending, 2); - assert_eq!(txq.top_transactions()[1].gas_price, U256::from(200)); - } - - #[test] - fn should_recalculate_height_when_removing_from_future() { - // given - let previous_nonce = default_account_details().nonce - U256::one(); - let mut txq = TransactionQueue::default(); - let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); - txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(previous_nonce)).unwrap(); - txq.add(tx2, TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(previous_nonce)).unwrap(); - assert_eq!(txq.status().future, 2); - - // when - txq.remove(&tx1.hash(), &|_| default_nonce() + 1.into(), RemovalReason::Invalid); - - // then - let stats = txq.status(); - assert_eq!(stats.future, 0); - assert_eq!(stats.pending, 1); - } - - #[test] - fn should_return_none_when_transaction_from_given_address_does_not_exist() { - // given - let txq = TransactionQueue::default(); - - // then - assert_eq!(txq.last_nonce(&Address::default()), None); - } - - #[test] - fn should_return_correct_nonce_when_transactions_from_given_address_exist() { - // given - let mut txq = TransactionQueue::default(); - let tx = new_tx_default(); - let from = tx.sender(); - let nonce = tx.nonce; - - // when - txq.add(tx, TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(nonce)).unwrap(); - - // then - assert_eq!(txq.last_nonce(&from), Some(nonce)); - } - - #[test] - fn should_remove_old_transaction_even_if_newer_transaction_was_not_known() { - // given - let mut txq = TransactionQueue::default(); - let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); - let (nonce1, nonce2) = (tx1.nonce, tx2.nonce); - - // Insert first transaction - txq.add(tx1, TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(nonce1)).unwrap(); - - // when - txq.cull(tx2.sender(), nonce2 + U256::one()); - - // then - assert!(txq.top_transactions().is_empty()); - } - - #[test] - fn should_return_valid_last_nonce_after_cull() { - // given - let mut txq = TransactionQueue::default(); - let (tx1, tx2) = new_tx_pair_default(4.into(), 0.into()); - let sender = tx1.sender(); - let (nonce1, nonce2) = (tx1.nonce, tx2.nonce); - - // when - // Insert first transaction - assert_eq!(txq.add(tx1, TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(nonce1)).unwrap(), transaction::ImportResult::Current); - // Second should go to future - assert_eq!(txq.add(tx2, TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(nonce1)).unwrap(), transaction::ImportResult::Future); - // Now block is imported - txq.cull(sender, nonce2 - U256::from(1)); - // tx2 should be not be promoted to current - assert_eq!(txq.status().pending, 0); - assert_eq!(txq.status().future, 1); - - // then - assert_eq!(txq.last_nonce(&sender), None); - } - - #[test] - fn should_return_true_if_there_is_local_transaction_pending() { - // given - let mut txq = TransactionQueue::default(); - let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); - assert_eq!(txq.has_local_pending_transactions(), false); - - // when - assert_eq!(txq.add(tx1, TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(), transaction::ImportResult::Current); - assert_eq!(txq.has_local_pending_transactions(), false); - assert_eq!(txq.add(tx2, TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap(), - transaction::ImportResult::Current); - - // then - assert_eq!(txq.has_local_pending_transactions(), true); - } - - #[test] - fn should_keep_right_order_in_future() { - // given - let mut txq = TransactionQueue::with_limits( - PrioritizationStrategy::GasPriceOnly, - 1, - usize::max_value(), - !U256::zero(), - !U256::zero() - ); - let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); - let prev_nonce = default_account_details().nonce - U256::one(); - - // when - assert_eq!(txq.add(tx2, TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(prev_nonce)).unwrap(), transaction::ImportResult::Future); - assert_eq!(txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(prev_nonce)).unwrap(), transaction::ImportResult::Future); - - // then - assert_eq!(txq.future.by_priority.len(), 1); - assert_eq!(txq.future.by_priority.iter().next().unwrap().hash, tx1.hash()); - } - - #[test] - fn should_return_correct_last_nonce() { - // given - let mut txq = TransactionQueue::default(); - let (tx1, tx2, tx2_2, tx3) = { - let keypair = Random.generate().unwrap(); - let secret = &keypair.secret(); - let nonce = 123.into(); - let gas = default_gas_val(); - let tx = new_unsigned_tx(nonce, gas, 1.into()); - let tx2 = new_unsigned_tx(nonce + 1.into(), gas, 1.into()); - let tx2_2 = new_unsigned_tx(nonce + 1.into(), gas, 5.into()); - let tx3 = new_unsigned_tx(nonce + 2.into(), gas, 1.into()); - - - (tx.sign(secret, None), tx2.sign(secret, None), tx2_2.sign(secret, None), tx3.sign(secret, None)) - }; - let sender = tx1.sender(); - txq.add(tx1, TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap(); - txq.add(tx2, TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap(); - txq.add(tx3, TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap(); - assert_eq!(txq.future.by_priority.len(), 0); - assert_eq!(txq.current.by_priority.len(), 3); - - // when - let res = txq.add(tx2_2, TransactionOrigin::Local, 0, None, &default_tx_provider()); - - // then - assert_eq!(txq.last_nonce(&sender).unwrap(), 125.into()); - assert_eq!(res.unwrap(), transaction::ImportResult::Current); - assert_eq!(txq.current.by_priority.len(), 3); - } - - #[test] - fn should_reject_transactions_below_base_gas() { - // given - let mut txq = TransactionQueue::default(); - let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); - let high_gas = 100_001.into(); - - // when - let res1 = txq.add(tx1, TransactionOrigin::Local, 0, None, &default_tx_provider()); - let res2 = txq.add(tx2, TransactionOrigin::Local, 0, None, &default_tx_provider().with_tx_gas_required(high_gas)); - - // then - assert_eq!(res1.unwrap(), transaction::ImportResult::Current); - assert_eq!(unwrap_tx_err(res2), transaction::Error::InsufficientGas { - minimal: 100_001.into(), - got: 100_000.into(), - }); - - } - - #[test] - fn should_clear_all_old_transactions() { - // given - let mut txq = TransactionQueue::default(); - let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); - let (tx3, tx4) = new_tx_pair_default(1.into(), 0.into()); - let next_nonce = |_: &Address| - AccountDetails { nonce: default_nonce() + U256::one(), balance: !U256::zero() }; - - // Insert all transactions - txq.add(tx1, TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - txq.add(tx2, TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - txq.add(tx3, TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - txq.add(tx4, TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - assert_eq!(txq.top_transactions().len(), 4); - - // when - txq.remove_old(&next_nonce, 0); - - // then - assert_eq!(txq.top_transactions().len(), 2); - } - - #[test] - fn should_remove_out_of_date_transactions_occupying_queue() { - // given - let mut txq = TransactionQueue::default(); - let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); - let (tx3, tx4) = new_tx_pair_default(2.into(), 0.into()); - - // Insert all transactions - txq.add(tx1.clone(), TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap(); - txq.add(tx2, TransactionOrigin::External, 5, None, &default_tx_provider()).unwrap(); - txq.add(tx3.clone(), TransactionOrigin::External, 10, None, &default_tx_provider()).unwrap(); - txq.add(tx4, TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - assert_eq!(txq.top_transactions().len(), 3); - assert_eq!(txq.future_transactions().len(), 1); - - // when - txq.remove_old(&default_account_details_for_addr, 9 + super::DEFAULT_QUEUING_PERIOD); - - // then - assert_eq!(txq.top_transactions().len(), 2); - assert_eq!(txq.future_transactions().len(), 0); - assert_eq!(txq.top_transactions(), vec![tx1, tx3]); - } - - #[test] - fn should_accept_local_service_transaction() { - // given - let tx = new_tx(123.into(), 0.into()); - let mut txq = TransactionQueue::default(); - txq.set_minimal_gas_price(100.into()); - - // when - txq.add(tx, TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap(); - - // then - assert_eq!(txq.top_transactions().len(), 1); - } - - #[test] - fn should_not_accept_external_service_transaction_if_sender_not_certified() { - // given - let tx1 = new_tx(123.into(), 0.into()); - let tx2 = new_tx(456.into(), 0.into()); - let mut txq = TransactionQueue::default(); - txq.set_minimal_gas_price(100.into()); - - // when - assert_eq!(unwrap_tx_err(txq.add(tx1, TransactionOrigin::External, 0, None, &default_tx_provider())), - transaction::Error::InsufficientGasPrice { - minimal: 100.into(), - got: 0.into(), - }); - assert_eq!(unwrap_tx_err(txq.add(tx2, TransactionOrigin::RetractedBlock, 0, None, &default_tx_provider())), - transaction::Error::InsufficientGasPrice { - minimal: 100.into(), - got: 0.into(), - }); - - // then - assert_eq!(txq.top_transactions().len(), 0); - } - - #[test] - fn should_not_accept_external_service_transaction_if_contract_returns_error() { - // given - let tx = new_tx(123.into(), 0.into()); - let mut txq = TransactionQueue::default(); - txq.set_minimal_gas_price(100.into()); - - // when - let details_provider = default_tx_provider().service_transaction_checker_returns_error("Contract error"); - assert_eq!(unwrap_tx_err(txq.add(tx, TransactionOrigin::External, 0, None, &details_provider)), - transaction::Error::InsufficientGasPrice { - minimal: 100.into(), - got: 0.into(), - }); - - // then - assert_eq!(txq.top_transactions().len(), 0); - } - - #[test] - fn should_accept_external_service_transaction_if_sender_is_certified() { - // given - let tx = new_tx(123.into(), 0.into()); - let mut txq = TransactionQueue::default(); - txq.set_minimal_gas_price(100.into()); - - // when - let details_provider = default_tx_provider().service_transaction_checker_accepts(true); - txq.add(tx, TransactionOrigin::External, 0, None, &details_provider).unwrap(); - - // then - assert_eq!(txq.top_transactions().len(), 1); - } - - #[test] - fn should_not_order_transactions_by_hash() { - // given - let secret1 = "0000000000000000000000000000000000000000000000000000000000000002".parse().unwrap(); - let secret2 = "0000000000000000000000000000000000000000000000000000000000000001".parse().unwrap(); - let tx1 = new_unsigned_tx(123.into(), default_gas_val(), 0.into()).sign(&secret1, None); - let tx2 = new_unsigned_tx(123.into(), default_gas_val(), 0.into()).sign(&secret2, None); - let mut txq = TransactionQueue::default(); - - // when - txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - txq.add(tx2, TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - - // then - assert_eq!(txq.top_transactions()[0], tx1); - assert_eq!(txq.top_transactions().len(), 2); - } - - #[test] - fn should_not_return_transactions_over_nonce_cap() { - // given - let keypair = Random.generate().unwrap(); - let mut txq = TransactionQueue::default(); - // when - for nonce in 123..130 { - let tx = new_unsigned_tx(nonce.into(), default_gas_val(), default_gas_price()).sign(keypair.secret(), None); - txq.add(tx, TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); - } - - // then - assert_eq!(txq.top_transactions_at(BlockNumber::max_value(), u64::max_value(), Some(127.into())).len(), 4); - } -} diff --git a/miner/src/work_notify.rs b/miner/src/work_notify.rs index 8ab65ed83246cdb881911a7ee5fde23fa1e33137..8825fd4b65f52ff2d1c78c110ef86952d90d78d7 100644 --- a/miner/src/work_notify.rs +++ b/miner/src/work_notify.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -17,19 +17,20 @@ //! Sends HTTP notifications to a list of URLs every time new work is available. extern crate ethash; +extern crate fetch; +extern crate parity_reactor; +extern crate url; extern crate hyper; -use self::hyper::header::ContentType; -use self::hyper::method::Method; -use self::hyper::client::{Request, Response, Client}; -use self::hyper::{Next, Url}; -use self::hyper::net::HttpStream; - +use self::fetch::{Fetch, Request, Client as FetchClient, Method}; +use self::parity_reactor::Remote; use self::ethash::SeedHashCompute; +use self::url::Url; +use self::hyper::header::ContentType; -use std::io::Write; use ethereum_types::{H256, U256}; use parking_lot::Mutex; +use futures::Future; /// Trait for notifying about new mining work pub trait NotifyWork : Send + Sync { @@ -40,13 +41,14 @@ pub trait NotifyWork : Send + Sync { /// POSTs info about new work to given urls. pub struct WorkPoster { urls: Vec, - client: Mutex>, + client: FetchClient, + remote: Remote, seed_compute: Mutex, } impl WorkPoster { /// Create new `WorkPoster`. - pub fn new(urls: &[String]) -> Self { + pub fn new(urls: &[String], fetch: FetchClient, remote: Remote) -> Self { let urls = urls.into_iter().filter_map(|u| { match Url::parse(u) { Ok(url) => Some(url), @@ -56,20 +58,13 @@ impl WorkPoster { } } }).collect(); - let client = WorkPoster::create_client(); WorkPoster { - client: Mutex::new(client), + client: fetch, + remote: remote, urls: urls, seed_compute: Mutex::new(SeedHashCompute::new()), } } - - fn create_client() -> Client { - Client::::configure() - .keep_alive(true) - .build() - .expect("Error creating HTTP client") - } } /// Convert an Ethash difficulty to the target boundary. Basically just `f(x) = 2^256 / x`. @@ -91,51 +86,16 @@ impl NotifyWork for WorkPoster { r#"{{ "result": ["0x{:x}","0x{:x}","0x{:x}","0x{:x}"] }}"#, pow_hash, seed_hash, target, number ); - let mut client = self.client.lock(); + for u in &self.urls { - if let Err(e) = client.request(u.clone(), PostHandler { body: body.clone() }) { + let u = u.clone(); + self.remote.spawn(self.client.fetch( + Request::new(u.clone(), Method::Post) + .with_header(ContentType::json()) + .with_body(body.clone()), Default::default() + ).map_err(move |e| { warn!("Error sending HTTP notification to {} : {}, retrying", u, e); - // TODO: remove this once https://github.com/hyperium/hyper/issues/848 is fixed - *client = WorkPoster::create_client(); - if let Err(e) = client.request(u.clone(), PostHandler { body: body.clone() }) { - warn!("Error sending HTTP notification to {} : {}", u, e); - } - } - } - } -} - -struct PostHandler { - body: String, -} - -impl hyper::client::Handler for PostHandler { - fn on_request(&mut self, request: &mut Request) -> Next { - request.set_method(Method::Post); - request.headers_mut().set(ContentType::json()); - Next::write() - } - - fn on_request_writable(&mut self, encoder: &mut hyper::Encoder) -> Next { - if let Err(e) = encoder.write_all(self.body.as_bytes()) { - trace!("Error posting work data: {}", e); + }).map(|_| ())); } - encoder.close(); - Next::read() - - } - - fn on_response(&mut self, _response: Response) -> Next { - Next::end() - } - - fn on_response_readable(&mut self, _decoder: &mut hyper::Decoder) -> Next { - Next::end() - } - - fn on_error(&mut self, err: hyper::Error) -> Next { - trace!("Error posting work data: {}", err); - Next::end() } } - diff --git a/nsis/installer.nsi b/nsis/installer.nsi deleted file mode 100644 index 011557234014307e8fe0dc15ccb3fc788adc3e03..0000000000000000000000000000000000000000 --- a/nsis/installer.nsi +++ /dev/null @@ -1,191 +0,0 @@ -!include WinMessages.nsh - -!define WND_CLASS "Parity" -!define WND_TITLE "Parity" -!define WAIT_MS 5000 -!define SYNC_TERM 0x00100001 - -!define APPNAME "Parity" -!define COMPANYNAME "Parity Technologies" -!define DESCRIPTION "Fast, light, robust Ethereum implementation" -!define VERSIONMAJOR 1 -!define VERSIONMINOR 11 -!define VERSIONBUILD 0 -!define ARGS "" -!define FIRST_START_ARGS "--mode=passive ui" - -!addplugindir .\ - -!define HELPURL "https://paritytech.github.io/wiki/" # "Support Information" link -!define UPDATEURL "https://github.com/paritytech/parity/releases" # "Product Updates" link -!define ABOUTURL "https://github.com/paritytech/parity" # "Publisher" link -!define INSTALLSIZE 26120 - -!define termMsg "Installer cannot stop running ${WND_TITLE}.$\nDo you want to terminate process?" -!define stopMsg "Stopping ${WND_TITLE} Application" - - -RequestExecutionLevel admin ;Require admin rights on NT6+ (When UAC is turned on) - -InstallDir "$PROGRAMFILES64\${COMPANYNAME}\${APPNAME}" - -LicenseData "..\LICENSE" -Name "${COMPANYNAME} ${APPNAME}" -Icon "logo.ico" -outFile "installer.exe" - -!include LogicLib.nsh - -page license -page directory -page instfiles - -!macro VerifyUserIsAdmin -UserInfo::GetAccountType -pop $0 -${If} $0 != "admin" ;Require admin rights on NT4+ - messageBox mb_iconstop "Administrator rights required!" - setErrorLevel 740 ;ERROR_ELEVATION_REQUIRED - quit -${EndIf} -!macroend - -!macro TerminateApp - Push $0 ; window handle - Push $1 - Push $2 ; process handle - DetailPrint "$(stopMsg)" - FindWindow $0 '${WND_CLASS}' '' - IntCmp $0 0 done - System::Call 'user32.dll::GetWindowThreadProcessId(i r0, *i .r1) i .r2' - System::Call 'kernel32.dll::OpenProcess(i ${SYNC_TERM}, i 0, i r1) i .r2' - SendMessage $0 ${WM_CLOSE} 0 0 /TIMEOUT=${TO_MS} - System::Call 'kernel32.dll::WaitForSingleObject(i r2, i ${WAIT_MS}) i .r1' - IntCmp $1 0 close - MessageBox MB_YESNOCANCEL|MB_ICONEXCLAMATION "$(termMsg)" /SD IDYES IDYES terminate IDNO close - System::Call 'kernel32.dll::CloseHandle(i r2) i .r1' - Quit - terminate: - System::Call 'kernel32.dll::TerminateProcess(i r2, i 0) i .r1' - close: - System::Call 'kernel32.dll::CloseHandle(i r2) i .r1' - done: - Pop $2 - Pop $1 - Pop $0 -!macroend - -function .onInit - setShellVarContext all - !insertmacro VerifyUserIsAdmin -functionEnd - -section "install" - # Files for the install directory - to build the installer, these should be in the same directory as the install script (this file) - setOutPath $INSTDIR - - # Close parity if running - !insertmacro TerminateApp - - # Files added here should be removed by the uninstaller (see section "uninstall") - file /oname=parity.exe ..\target\x86_64-pc-windows-msvc\release\parity.exe - file /oname=parity-evm.exe ..\target\x86_64-pc-windows-msvc\release\parity-evm.exe - file /oname=ethstore.exe ..\target\x86_64-pc-windows-msvc\release\ethstore.exe - file /oname=ethkey.exe ..\target\x86_64-pc-windows-msvc\release\ethkey.exe - file /oname=ptray.exe ..\windows\ptray\x64\Release\ptray.exe - - file "logo.ico" - # Add any other files for the install directory (license files, app data, etc) here - - # Uninstaller - See function un.onInit and section "uninstall" for configuration - writeUninstaller "$INSTDIR\uninstall.exe" - - # Start Menu - createDirectory "$SMPROGRAMS\${COMPANYNAME}" - delete "$SMPROGRAMS\${COMPANYNAME}\${APPNAME}.lnk" - createShortCut "$SMPROGRAMS\${COMPANYNAME}\${APPNAME} Ethereum.lnk" "$INSTDIR\ptray.exe" "ui" "$INSTDIR\logo.ico" - createShortCut "$DESKTOP\${APPNAME} Ethereum.lnk" "$INSTDIR\ptray.exe" "ui" "$INSTDIR\logo.ico" - - # Firewall remove rules if exists - SimpleFC::AdvRemoveRule "Parity incoming peers (TCP:30303)" - SimpleFC::AdvRemoveRule "Parity outgoing peers (TCP:30303)" - SimpleFC::AdvRemoveRule "Parity web queries (TCP:80)" - SimpleFC::AdvRemoveRule "Parity UDP discovery (UDP:30303)" - - # Firewall exception rules - SimpleFC::AdvAddRule "Parity incoming peers (TCP:30303)" "" 6 1 1 2147483647 1 "$INSTDIR\parity.exe" "" "" "Parity" 30303 "" "" "" - SimpleFC::AdvAddRule "Parity outgoing peers (TCP:30303)" "" 6 2 1 2147483647 1 "$INSTDIR\parity.exe" "" "" "Parity" "" 30303 "" "" - SimpleFC::AdvAddRule "Parity UDP discovery (UDP:30303)" "" 17 2 1 2147483647 1 "$INSTDIR\parity.exe" "" "" "Parity" "" 30303 "" "" - - # Registry information for add/remove programs - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "DisplayName" "${APPNAME} - ${DESCRIPTION}" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "UninstallString" "$\"$INSTDIR\uninstall.exe$\"" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "QuietUninstallString" "$\"$INSTDIR\uninstall.exe$\" /S" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "InstallLocation" "$\"$INSTDIR$\"" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "DisplayIcon" "$\"$INSTDIR\logo.ico$\"" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "Publisher" "${COMPANYNAME}" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "HelpLink" "$\"${HELPURL}$\"" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "URLUpdateInfo" "$\"${UPDATEURL}$\"" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "URLInfoAbout" "$\"${ABOUTURL}$\"" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "DisplayVersion" "${VERSIONMAJOR}.${VERSIONMINOR}.${VERSIONBUILD}" - WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "VersionMajor" ${VERSIONMAJOR} - WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "VersionMinor" ${VERSIONMINOR} - # There is no option for modifying or repairing the install - WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "NoModify" 1 - WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "NoRepair" 1 - # Set the INSTALLSIZE constant (!defined at the top of this script) so Add/Remove Programs can accurately report the size - WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "EstimatedSize" ${INSTALLSIZE} - - WriteRegStr HKEY_CURRENT_USER "Software\Microsoft\Windows\CurrentVersion\Run" ${APPNAME} "$INSTDIR\ptray.exe ${ARGS}" - DeleteRegValue HKLM "Software\Microsoft\Windows\CurrentVersion\Run" "${APPNAME}" - ExecShell "" "$INSTDIR\ptray.exe" "${FIRST_START_ARGS}" -sectionEnd - -# Uninstaller - -function un.onInit - SetShellVarContext all - - #Verify the uninstaller - last chance to back out - MessageBox MB_OKCANCEL "Permanently remove ${APPNAME}?" IDOK next - Abort - - next: - !insertmacro VerifyUserIsAdmin -functionEnd - -section "uninstall" - !insertmacro TerminateApp - # Remove Start Menu launcher - delete "$SMPROGRAMS\${COMPANYNAME}\${APPNAME}.lnk" - delete "$SMPROGRAMS\${COMPANYNAME}\${APPNAME} Ethereum.lnk" - delete "$DESKTOP\${APPNAME} Ethereum.lnk" - - # Try to remove the Start Menu folder - this will only happen if it is empty - rmDir "$SMPROGRAMS\${COMPANYNAME}" - - # Remove files - delete $INSTDIR\parity.exe - delete $INSTDIR\parity-evm.exe - delete $INSTDIR\ethstore.exe - delete $INSTDIR\ethkey.exe - delete $INSTDIR\ptray.exe - delete $INSTDIR\logo.ico - - # Always delete uninstaller as the last action - delete $INSTDIR\uninstall.exe - - # Try to remove the install directory - this will only happen if it is empty - rmDir $INSTDIR - - # Firewall exception rules - SimpleFC::AdvRemoveRule "Parity incoming peers (TCP:30303)" - SimpleFC::AdvRemoveRule "Parity outgoing peers (TCP:30303)" - SimpleFC::AdvRemoveRule "Parity web queries (TCP:80)" - SimpleFC::AdvRemoveRule "Parity UDP discovery (UDP:30303)" - - # Remove uninstaller information from the registry - DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" - DeleteRegValue HKLM "Software\Microsoft\Windows\CurrentVersion\Run" "${APPNAME}" - DeleteRegValue HKCU "Software\Microsoft\Windows\CurrentVersion\Run" "${APPNAME}" -sectionEnd diff --git a/nsis/logo.ico b/nsis/logo.ico deleted file mode 100644 index cda99eef8b3668827befd6a13ff8268c096ceae4..0000000000000000000000000000000000000000 Binary files a/nsis/logo.ico and /dev/null differ diff --git a/parity-clib-example/CMakeLists.txt b/parity-clib-example/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..143d014e322bc4ea523a44b6b37f9199d710eeb3 --- /dev/null +++ b/parity-clib-example/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 3.5) +include(ExternalProject) + +include_directories("${CMAKE_SOURCE_DIR}/../parity-clib") + +add_executable(parity-example main.cpp) + +ExternalProject_Add( + libparity + DOWNLOAD_COMMAND "" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + COMMAND cargo build -p parity-clib # Note: use --release in a real project + BINARY_DIR "${CMAKE_SOURCE_DIR}/../target" + INSTALL_COMMAND "" + LOG_BUILD ON) + +add_dependencies(parity-example libparity) +target_link_libraries(parity-example "${CMAKE_SOURCE_DIR}/../target/debug/libparity.so") diff --git a/parity-clib-example/main.cpp b/parity-clib-example/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c5e83d06492a3ff29a755b64a13f662ff2c711ce --- /dev/null +++ b/parity-clib-example/main.cpp @@ -0,0 +1,57 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +#include +#include +#include +#include +#include +#include + +void on_restart(void*, const char*, size_t) {} + +int main() { + ParityParams cfg = { 0 }; + cfg.on_client_restart_cb = on_restart; + + const char* args[] = {"--no-ipc"}; + size_t str_lens[] = {8}; + if (parity_config_from_cli(args, str_lens, 1, &cfg.configuration) != 0) { + return 1; + } + + void* parity; + if (parity_start(&cfg, &parity) != 0) { + return 1; + } + + const char* rpc = "{\"method\":\"parity_versionInfo\",\"params\":[],\"id\":1,\"jsonrpc\":\"2.0\"}"; + size_t out_len = 256; + char* out = (char*)malloc(out_len + 1); + if (parity_rpc(parity, rpc, strlen(rpc), out, &out_len)) { + return 1; + } + out[out_len] = '\0'; + printf("RPC output: %s", out); + free(out); + + sleep(5); + if (parity != NULL) { + parity_destroy(parity); + } + + return 0; +} diff --git a/parity-clib/Cargo.toml b/parity-clib/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..001f954c211443ad13f7b5af57aba798062db978 --- /dev/null +++ b/parity-clib/Cargo.toml @@ -0,0 +1,17 @@ +[package] +description = "C bindings for the Parity Ethereum client" +name = "parity-clib" +version = "1.12.0" +license = "GPL-3.0" +authors = ["Parity Technologies "] + +[lib] +name = "parity" +crate-type = ["cdylib", "staticlib"] + +[dependencies] +parity = { path = "../", default-features = false } + +[features] +default = [] +final = ["parity/final"] diff --git a/parity-clib/parity.h b/parity-clib/parity.h new file mode 100644 index 0000000000000000000000000000000000000000..f647395ce9a031cb4eef427f899aa26164284740 --- /dev/null +++ b/parity-clib/parity.h @@ -0,0 +1,109 @@ +// Copyright 2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +#ifndef _PARITY_H_INCLUDED_ +#define _PARITY_H_INCLUDED_ + +#include + +/// Parameters to pass to `parity_start`. +struct ParityParams { + /// Configuration object, as handled by the `parity_config_*` functions. + /// Note that calling `parity_start` will destroy the configuration object (even on failure). + void *configuration; + + /// Callback function to call when the client receives an RPC request to change its chain spec. + /// + /// Will only be called if you enable the `--can-restart` flag. + /// + /// The first parameter of the callback is the value of `on_client_restart_cb_custom`. + /// The second and third parameters of the callback are the string pointer and length. + void (*on_client_restart_cb)(void* custom, const char* new_chain, size_t new_chain_len); + + /// Custom parameter passed to the `on_client_restart_cb` callback as first parameter. + void *on_client_restart_cb_custom; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +/// Builds a new configuration object by parsing a list of CLI arguments. +/// +/// The first two parameters are string pointers and string lengths. They must have a length equal +/// to `len`. The strings don't need to be zero-terminated. +/// +/// On success, the produced object will be written to the `void*` pointed by `out`. +/// +/// Returns 0 on success, and non-zero on error. +/// +/// # Example +/// +/// ```no_run +/// void* cfg; +/// const char *args[] = {"--light", "--can-restart"}; +/// size_t str_lens[] = {7, 13}; +/// if (parity_config_from_cli(args, str_lens, 2, &cfg) != 0) { +/// return 1; +/// } +/// ``` +/// +int parity_config_from_cli(char const* const* args, size_t const* arg_lens, size_t len, void** out); + +/// Destroys a configuration object created earlier. +/// +/// **Important**: You probably don't need to call this function. Calling `parity_start` destroys +/// the configuration object as well (even on failure). +void parity_config_destroy(void* cfg); + +/// Starts the parity client in background threads. Returns a pointer to a struct that represents +/// the running client. Can also return NULL if the execution completes instantly. +/// +/// **Important**: The configuration object passed inside `cfg` is destroyed when you +/// call `parity_start` (even on failure). +/// +/// On success, the produced object will be written to the `void*` pointed by `out`. +/// +/// Returns 0 on success, and non-zero on error. +int parity_start(const ParityParams* params, void** out); + +/// Destroys the parity client created with `parity_start`. +/// +/// **Warning**: `parity_start` can return NULL if execution finished instantly, in which case you +/// must not call this function. +void parity_destroy(void* parity); + +/// Performs an RPC request. +/// +/// Blocks the current thread until the request is finished. You are therefore encouraged to spawn +/// a new thread for each RPC request that requires accessing the blockchain. +/// +/// - `rpc` and `len` must contain the JSON string representing the RPC request. +/// - `out_str` and `out_len` point to a buffer where the output JSON result will be stored. If the +/// buffer is not large enough, the function fails. +/// - `out_len` will receive the final length of the string. +/// - On success, the function returns 0. On failure, it returns 1. +/// +/// **Important**: Keep in mind that this function doesn't write any null terminator on the output +/// string. +/// +int parity_rpc(void* parity, const char* rpc, size_t len, char* out_str, size_t* out_len); + +#ifdef __cplusplus +} +#endif + +#endif // include guard diff --git a/parity-clib/src/lib.rs b/parity-clib/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..f7a98f811d11ea53888ced9b4e830545f5094ae3 --- /dev/null +++ b/parity-clib/src/lib.rs @@ -0,0 +1,164 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Note that all the structs and functions here are documented in `parity.h`, to avoid +//! duplicating documentation. + +extern crate parity; + +use std::os::raw::{c_char, c_void, c_int}; +use std::panic; +use std::ptr; +use std::slice; +use std::str; + +#[repr(C)] +pub struct ParityParams { + pub configuration: *mut c_void, + pub on_client_restart_cb: Option, + pub on_client_restart_cb_custom: *mut c_void, +} + +#[no_mangle] +pub extern fn parity_config_from_cli(args: *const *const c_char, args_lens: *const usize, len: usize, output: *mut *mut c_void) -> c_int { + unsafe { + panic::catch_unwind(|| { + *output = ptr::null_mut(); + + let args = { + let arg_ptrs = slice::from_raw_parts(args, len); + let arg_lens = slice::from_raw_parts(args_lens, len); + + let mut args = Vec::with_capacity(len + 1); + args.push("parity".to_owned()); + + for (&arg, &len) in arg_ptrs.iter().zip(arg_lens.iter()) { + let string = slice::from_raw_parts(arg as *const u8, len); + match String::from_utf8(string.to_owned()) { + Ok(a) => args.push(a), + Err(_) => return 1, + }; + } + + args + }; + + match parity::Configuration::parse_cli(&args) { + Ok(mut cfg) => { + // Always disable the auto-updater when used as a library. + cfg.args.arg_auto_update = "none".to_owned(); + + let cfg = Box::into_raw(Box::new(cfg)); + *output = cfg as *mut _; + 0 + }, + Err(_) => { + 1 + }, + } + }).unwrap_or(1) + } +} + +#[no_mangle] +pub extern fn parity_config_destroy(cfg: *mut c_void) { + unsafe { + let _ = panic::catch_unwind(|| { + let _cfg = Box::from_raw(cfg as *mut parity::Configuration); + }); + } +} + +#[no_mangle] +pub extern fn parity_start(cfg: *const ParityParams, output: *mut *mut c_void) -> c_int { + unsafe { + panic::catch_unwind(|| { + *output = ptr::null_mut(); + let cfg: &ParityParams = &*cfg; + + let config = Box::from_raw(cfg.configuration as *mut parity::Configuration); + + let on_client_restart_cb = { + struct Cb(Option, *mut c_void); + unsafe impl Send for Cb {} + unsafe impl Sync for Cb {} + impl Cb { + fn call(&self, new_chain: String) { + if let Some(ref cb) = self.0 { + cb(self.1, new_chain.as_bytes().as_ptr() as *const _, new_chain.len()) + } + } + } + let cb = Cb(cfg.on_client_restart_cb, cfg.on_client_restart_cb_custom); + move |new_chain: String| { cb.call(new_chain); } + }; + + let action = match parity::start(*config, on_client_restart_cb, || {}) { + Ok(action) => action, + Err(_) => return 1, + }; + + match action { + parity::ExecutionAction::Instant(Some(s)) => { println!("{}", s); 0 }, + parity::ExecutionAction::Instant(None) => 0, + parity::ExecutionAction::Running(client) => { + *output = Box::into_raw(Box::::new(client)) as *mut c_void; + 0 + } + } + }).unwrap_or(1) + } +} + +#[no_mangle] +pub extern fn parity_destroy(client: *mut c_void) { + unsafe { + let _ = panic::catch_unwind(|| { + let client = Box::from_raw(client as *mut parity::RunningClient); + client.shutdown(); + }); + } +} + +#[no_mangle] +pub extern fn parity_rpc(client: *mut c_void, query: *const char, len: usize, out_str: *mut c_char, out_len: *mut usize) -> c_int { + unsafe { + panic::catch_unwind(|| { + let client: &mut parity::RunningClient = &mut *(client as *mut parity::RunningClient); + + let query_str = { + let string = slice::from_raw_parts(query as *const u8, len); + match str::from_utf8(string) { + Ok(a) => a, + Err(_) => return 1, + } + }; + + if let Some(output) = client.rpc_query_sync(query_str) { + let q_out_len = output.as_bytes().len(); + if *out_len < q_out_len { + return 1; + } + + ptr::copy_nonoverlapping(output.as_bytes().as_ptr(), out_str as *mut u8, q_out_len); + *out_len = q_out_len; + 0 + } else { + 1 + } + }).unwrap_or(1) + } +} diff --git a/parity/account.rs b/parity/account.rs index 3db1844a1b4d55dbca48c9f4246a7820fe430137..c2f15546e6574e0ed1b69722abde0b1c1bff215a 100644 --- a/parity/account.rs +++ b/parity/account.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -94,7 +94,7 @@ fn new(n: NewAccount) -> Result { 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{:?}", new_account)) + Ok(format!("0x{:x}", new_account)) } fn list(list_cmd: ListAccounts) -> Result { @@ -103,7 +103,7 @@ fn list(list_cmd: ListAccounts) -> Result { 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{:?}", a)) + .map(|a| format!("0x{:x}", a)) .collect::>() .join("\n"); diff --git a/parity/blockchain.rs b/parity/blockchain.rs index 9dab9069d78d89984a848ea8e8f74b1449e39fa0..d33ac1eacbaefe29d65b0142fd4f2bf15e9d8f36 100644 --- a/parity/blockchain.rs +++ b/parity/blockchain.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -25,20 +25,20 @@ use hash::{keccak, KECCAK_NULL_RLP}; use ethereum_types::{U256, H256, Address}; use bytes::ToPretty; use rlp::PayloadInfo; +use ethcore::account_provider::AccountProvider; use ethcore::client::{Mode, DatabaseCompactionProfile, VMType, BlockImportError, Nonce, Balance, BlockChainClient, BlockId, BlockInfo, ImportBlock}; -use ethcore::db::NUM_COLUMNS; -use ethcore::error::ImportError; +use ethcore::error::{ImportErrorKind, BlockImportErrorKind}; use ethcore::miner::Miner; use ethcore::verification::queue::VerifierSettings; use ethcore_service::ClientService; use cache::CacheConfig; use informant::{Informant, FullNodeInformantData, MillisecondDuration}; -use kvdb_rocksdb::{Database, DatabaseConfig}; use params::{SpecType, Pruning, Switch, tracing_switch_to_bool, fatdb_switch_to_bool}; use helpers::{to_client_config, execute_upgrades}; use dir::Directories; use user_defaults::UserDefaults; -use fdlimit; +use ethcore_private_tx; +use db; #[derive(Debug, PartialEq)] pub enum DataFormat { @@ -177,8 +177,6 @@ fn execute_import_light(cmd: ImportBlockchain) -> Result<(), String> { // load user defaults let user_defaults = UserDefaults::load(&user_defaults_path)?; - fdlimit::raise_fd_limit(); - // select pruning algorithm let algorithm = cmd.pruning.to_algorithm(&user_defaults); @@ -186,8 +184,7 @@ fn execute_import_light(cmd: ImportBlockchain) -> Result<(), String> { let client_path = db_dirs.client_path(algorithm); // execute upgrades - let compaction = cmd.compaction.compaction_profile(db_dirs.db_root_path().as_path()); - execute_upgrades(&cmd.dirs.base, &db_dirs, algorithm, compaction)?; + execute_upgrades(&cmd.dirs.base, &db_dirs, algorithm, &cmd.compaction)?; // create dirs used by parity cmd.dirs.create_dirs(false, false, false)?; @@ -208,19 +205,10 @@ fn execute_import_light(cmd: ImportBlockchain) -> Result<(), String> { config.queue.verifier_settings = cmd.verifier_settings; // initialize database. - let db = { - let db_config = DatabaseConfig { - memory_budget: Some(cmd.cache_config.blockchain() as usize * 1024 * 1024), - compaction: compaction, - wal: cmd.wal, - .. DatabaseConfig::with_columns(NUM_COLUMNS) - }; - - Arc::new(Database::open( - &db_config, - &client_path.to_str().expect("DB path could not be converted to string.") - ).map_err(|e| format!("Failed to open database: {}", e))?) - }; + let db = db::open_db(&client_path.to_str().expect("DB path could not be converted to string."), + &cmd.cache_config, + &cmd.compaction, + cmd.wal)?; // TODO: could epoch signals be avilable at the end of the file? let fetch = ::light::client::fetch::unavailable(); @@ -256,7 +244,7 @@ fn execute_import_light(cmd: ImportBlockchain) -> Result<(), String> { let do_import = |bytes: Vec| { while client.queue_info().is_full() { sleep(Duration::from_secs(1)); } - let header: ::ethcore::header::Header = ::rlp::UntrustedRlp::new(&bytes).val_at(0) + let header: ::ethcore::header::Header = ::rlp::Rlp::new(&bytes).val_at(0) .map_err(|e| format!("Bad block: {}", e))?; if client.best_block_header().number() >= header.number() { return Ok(()) } @@ -266,7 +254,7 @@ fn execute_import_light(cmd: ImportBlockchain) -> Result<(), String> { } match client.import_header(header) { - Err(BlockImportError::Import(ImportError::AlreadyInChain)) => { + Err(BlockImportError(BlockImportErrorKind::Import(ImportErrorKind::AlreadyInChain), _)) => { trace!("Skipping block already in chain."); } Err(e) => { @@ -336,8 +324,6 @@ fn execute_import(cmd: ImportBlockchain) -> Result<(), String> { // load user defaults let mut user_defaults = UserDefaults::load(&user_defaults_path)?; - fdlimit::raise_fd_limit(); - // select pruning algorithm let algorithm = cmd.pruning.to_algorithm(&user_defaults); @@ -352,7 +338,7 @@ fn execute_import(cmd: ImportBlockchain) -> Result<(), String> { let snapshot_path = db_dirs.snapshot_path(); // execute upgrades - execute_upgrades(&cmd.dirs.base, &db_dirs, algorithm, cmd.compaction.compaction_profile(db_dirs.db_root_path().as_path()))?; + execute_upgrades(&cmd.dirs.base, &db_dirs, algorithm, &cmd.compaction)?; // create dirs used by parity cmd.dirs.create_dirs(false, false, false)?; @@ -376,14 +362,23 @@ fn execute_import(cmd: ImportBlockchain) -> Result<(), String> { client_config.queue.verifier_settings = cmd.verifier_settings; + let client_db = db::open_client_db(&client_path, &client_config)?; + let restoration_db_handler = db::restoration_db_handler(&client_path, &client_config); + // build client let service = ClientService::start( client_config, &spec, - &client_path, + client_db, &snapshot_path, + restoration_db_handler, &cmd.dirs.ipc_path(), - Arc::new(Miner::with_spec(&spec)), + // TODO [ToDr] don't use test miner here + // (actually don't require miner at all) + Arc::new(Miner::new_for_tests(&spec, None)), + Arc::new(AccountProvider::transient_provider()), + Box::new(ethcore_private_tx::NoopEncryptor), + Default::default(), ).map_err(|e| format!("Client service error: {:?}", e))?; // free up the spec in memory. @@ -428,7 +423,7 @@ fn execute_import(cmd: ImportBlockchain) -> Result<(), String> { let do_import = |bytes| { while client.queue_info().is_full() { sleep(Duration::from_secs(1)); } match client.import_block(bytes) { - Err(BlockImportError::Import(ImportError::AlreadyInChain)) => { + Err(BlockImportError(BlockImportErrorKind::Import(ImportErrorKind::AlreadyInChain), _)) => { trace!("Skipping block already in chain."); } Err(e) => { @@ -518,8 +513,6 @@ fn start_client( // load user defaults let user_defaults = UserDefaults::load(&user_defaults_path)?; - fdlimit::raise_fd_limit(); - // select pruning algorithm let algorithm = pruning.to_algorithm(&user_defaults); @@ -537,7 +530,7 @@ fn start_client( let snapshot_path = db_dirs.snapshot_path(); // execute upgrades - execute_upgrades(&dirs.base, &db_dirs, algorithm, compaction.compaction_profile(db_dirs.db_root_path().as_path()))?; + execute_upgrades(&dirs.base, &db_dirs, algorithm, &compaction)?; // create dirs used by parity dirs.create_dirs(false, false, false)?; @@ -559,13 +552,22 @@ fn start_client( true, ); + let client_db = db::open_client_db(&client_path, &client_config)?; + let restoration_db_handler = db::restoration_db_handler(&client_path, &client_config); + let service = ClientService::start( client_config, &spec, - &client_path, + client_db, &snapshot_path, + restoration_db_handler, &dirs.ipc_path(), - Arc::new(Miner::with_spec(&spec)), + // It's fine to use test version here, + // since we don't care about miner parameters at all + Arc::new(Miner::new_for_tests(&spec, None)), + Arc::new(AccountProvider::transient_provider()), + Box::new(ethcore_private_tx::NoopEncryptor), + Default::default(), ).map_err(|e| format!("Client service error: {:?}", e))?; drop(spec); diff --git a/parity/cache.rs b/parity/cache.rs index 0bf0717a30c61dd3874a7e7f2147c56b5012ccde..5848e404c2a86a0c85ad3c468f9f5047732c4935 100644 --- a/parity/cache.rs +++ b/parity/cache.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/parity/cli/mod.rs b/parity/cli/mod.rs index 5ae6c8d3daf6daf3d07da911ebf974bd3cc07f8b..676f591f428897080752490c2bdb0c92c495c756 100644 --- a/parity/cli/mod.rs +++ b/parity/cli/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -26,10 +26,6 @@ usage! { // Arguments must start with arg_ // Flags must start with flag_ - CMD cmd_ui { - "Manage ui", - } - CMD cmd_dapp { "Manage dapps", @@ -240,10 +236,6 @@ usage! { { // Global flags and arguments ["Operating Options"] - FLAG flag_public_node: (bool) = false, or |c: &Config| c.parity.as_ref()?.public_node.clone(), - "--public-node", - "Start Parity as a public web server. Account storage and transaction signing will be delegated to the UI.", - FLAG flag_no_download: (bool) = false, or |c: &Config| c.parity.as_ref()?.no_download.clone(), "--no-download", "Normally new releases will be downloaded ready for updating. This disables it. Not recommended.", @@ -280,13 +272,21 @@ usage! { "--auto-update=[SET]", "Set a releases set to automatically update and install. SET can be one of: all - All updates in the our release track; critical - Only consensus/security updates; none - No updates will be auto-installed.", + ARG arg_auto_update_delay: (u16) = 100u16, or |c: &Config| c.parity.as_ref()?.auto_update_delay.clone(), + "--auto-update-delay=[NUM]", + "Specify the maximum number of blocks used for randomly delaying updates.", + + ARG arg_auto_update_check_frequency: (u16) = 20u16, or |c: &Config| c.parity.as_ref()?.auto_update_check_frequency.clone(), + "--auto-update-check-frequency=[NUM]", + "Specify the number of blocks between each auto-update check.", + ARG arg_release_track: (String) = "current", or |c: &Config| c.parity.as_ref()?.release_track.clone(), "--release-track=[TRACK]", "Set which release track we should use for updates. TRACK can be one of: stable - Stable releases; beta - Beta releases; nightly - Nightly releases (unstable); testing - Testing releases (do not use); current - Whatever track this executable was released on.", ARG arg_chain: (String) = "foundation", or |c: &Config| c.parity.as_ref()?.chain.clone(), "--chain=[CHAIN]", - "Specify the blockchain type. CHAIN may be either a JSON chain specification file or olympic, frontier, homestead, mainnet, morden, ropsten, classic, expanse, musicoin, ellaism, testnet, kovan or dev.", + "Specify the blockchain type. CHAIN may be either a JSON chain specification file or olympic, frontier, homestead, mainnet, morden, ropsten, classic, expanse, tobalaba, musicoin, ellaism, easthub, social, testnet, kovan or dev.", ARG arg_keys_path: (String) = "$BASE/keys", or |c: &Config| c.parity.as_ref()?.keys_path.clone(), "--keys-path=[PATH]", @@ -307,7 +307,7 @@ usage! { ["Convenience options"] FLAG flag_unsafe_expose: (bool) = false, or |c: &Config| c.misc.as_ref()?.unsafe_expose, "--unsafe-expose", - "All servers will listen on external interfaces and will be remotely accessible. It's equivalent with setting the following: --{{ws,jsonrpc,ui,ipfs,secret_store,stratum}}-interface=all --*-hosts=all This option is UNSAFE and should be used with great care!", + "All servers will listen on external interfaces and will be remotely accessible. It's equivalent with setting the following: --[ws,jsonrpc,ui,ipfs-api,secretstore,stratum,dapps,secretstore-http]-interface=all --*-hosts=all This option is UNSAFE and should be used with great care!", ARG arg_config: (String) = "$BASE/config.toml", or |_| None, "-c, --config=[CONFIG]", @@ -342,36 +342,40 @@ usage! { "--password=[FILE]...", "Provide a file containing a password for unlocking an account. Leading and trailing whitespace is trimmed.", - ["UI options"] - FLAG flag_force_ui: (bool) = false, or |c: &Config| c.ui.as_ref()?.force.clone(), - "--force-ui", - "Enable Trusted UI WebSocket endpoint, even when --unlock is in use.", + ["Private transactions options"] + FLAG flag_private_enabled: (bool) = false, or |c: &Config| c.private_tx.as_ref()?.enabled, + "--private-tx-enabled", + "Enable private transactions.", - FLAG flag_no_ui: (bool) = false, or |c: &Config| c.ui.as_ref()?.disable.clone(), - "--no-ui", - "Disable Trusted UI WebSocket endpoint.", + ARG arg_private_signer: (Option) = None, or |c: &Config| c.private_tx.as_ref()?.signer.clone(), + "--private-signer=[ACCOUNT]", + "Specify the account for signing public transaction created upon verified private transaction.", - // NOTE [todr] For security reasons don't put this to config files - FLAG flag_ui_no_validation: (bool) = false, or |_| None, - "--ui-no-validation", - "Disable Origin and Host headers validation for Trusted UI. WARNING: INSECURE. Used only for development.", + ARG arg_private_validators: (Option) = None, or |c: &Config| c.private_tx.as_ref()?.validators.as_ref().map(|vec| vec.join(",")), + "--private-validators=[ACCOUNTS]", + "Specify the accounts for validating private transactions. ACCOUNTS is a comma-delimited list of addresses.", - ARG arg_ui_interface: (String) = "local", or |c: &Config| c.ui.as_ref()?.interface.clone(), - "--ui-interface=[IP]", - "Specify the hostname portion of the Trusted UI server, IP should be an interface's IP address, or local.", + ARG arg_private_account: (Option) = None, or |c: &Config| c.private_tx.as_ref()?.account.clone(), + "--private-account=[ACCOUNT]", + "Specify the account for signing requests to secret store.", - ARG arg_ui_hosts: (String) = "none", or |c: &Config| c.ui.as_ref()?.hosts.as_ref().map(|vec| vec.join(",")), - "--ui-hosts=[HOSTS]", - "List of allowed Host header values. This option will validate the Host header sent by the browser, it is additional security against some attack vectors. Special options: \"all\", \"none\",.", + ARG arg_private_sstore_url: (Option) = None, or |c: &Config| c.private_tx.as_ref()?.sstore_url.clone(), + "--private-sstore-url=[URL]", + "Specify secret store URL used for encrypting private transactions.", + + ARG arg_private_sstore_threshold: (Option) = None, or |c: &Config| c.private_tx.as_ref()?.sstore_threshold.clone(), + "--private-sstore-threshold=[NUM]", + "Specify secret store threshold used for encrypting private transactions.", + ARG arg_private_passwords: (Option) = None, or |c: &Config| c.private_tx.as_ref()?.passwords.clone(), + "--private-passwords=[FILE]...", + "Provide a file containing passwords for unlocking accounts (signer, private account, validators).", + + ["UI options"] ARG arg_ui_path: (String) = "$BASE/signer", or |c: &Config| c.ui.as_ref()?.path.clone(), "--ui-path=[PATH]", "Specify directory where Trusted UIs tokens should be stored.", - ARG arg_ui_port: (u16) = 8180u16, or |c: &Config| c.ui.as_ref()?.port.clone(), - "--ui-port=[PORT]", - "Specify the port of Trusted UI server.", - ["Networking options"] FLAG flag_no_warp: (bool) = false, or |c: &Config| c.network.as_ref()?.warp.clone().map(|w| !w), "--no-warp", @@ -387,21 +391,29 @@ usage! { FLAG flag_no_ancient_blocks: (bool) = false, or |_| None, "--no-ancient-blocks", - "Disable downloading old blocks after snapshot restoration or warp sync.", + "Disable downloading old blocks after snapshot restoration or warp sync. Not recommended.", FLAG flag_no_serve_light: (bool) = false, or |c: &Config| c.network.as_ref()?.no_serve_light.clone(), "--no-serve-light", "Disable serving of light peers.", + ARG arg_warp_barrier: (Option) = None, or |c: &Config| c.network.as_ref()?.warp_barrier.clone(), + "--warp-barrier=[NUM]", + "When warp enabled never attempt regular sync before warping to block NUM.", + ARG arg_port: (u16) = 30303u16, or |c: &Config| c.network.as_ref()?.port.clone(), "--port=[PORT]", "Override the port on which the node should listen.", - ARG arg_min_peers: (u16) = 25u16, or |c: &Config| c.network.as_ref()?.min_peers.clone(), + ARG arg_interface: (String) = "all", or |c: &Config| c.network.as_ref()?.interface.clone(), + "--interface=[IP]", + "Network interfaces. Valid values are 'all', 'local' or the ip of the interface you want parity to listen to.", + + ARG arg_min_peers: (Option) = None, or |c: &Config| c.network.as_ref()?.min_peers.clone(), "--min-peers=[NUM]", "Try to maintain at least NUM peers.", - ARG arg_max_peers: (u16) = 50u16, or |c: &Config| c.network.as_ref()?.max_peers.clone(), + ARG arg_max_peers: (Option) = None, or |c: &Config| c.network.as_ref()?.max_peers.clone(), "--max-peers=[NUM]", "Allow up to NUM peers.", @@ -450,9 +462,9 @@ usage! { "--jsonrpc-interface=[IP]", "Specify the hostname portion of the JSONRPC API server, IP should be an interface's IP address, or all (all interfaces) or local.", - ARG arg_jsonrpc_apis: (String) = "web3,eth,pubsub,net,parity,parity_pubsub,traces,rpc,shh,shh_pubsub", or |c: &Config| c.rpc.as_ref()?.apis.as_ref().map(|vec| vec.join(",")), + ARG arg_jsonrpc_apis: (String) = "web3,eth,pubsub,net,parity,private,parity_pubsub,traces,rpc,shh,shh_pubsub", or |c: &Config| c.rpc.as_ref()?.apis.as_ref().map(|vec| vec.join(",")), "--jsonrpc-apis=[APIS]", - "Specify the APIs available through the JSONRPC interface. APIS is a comma-delimited list of API name. Possible name are all, safe, web3, eth, net, personal, parity, parity_set, traces, rpc, parity_accounts, pubsub, parity_pubsub, shh, shh_pubsub, signer, secretstore. You can also disable a specific API by putting '-' in the front: all,-personal.", + "Specify the APIs available through the JSONRPC interface using a comma-delimited list of API names. Possible names are: all, safe, web3, net, eth, pubsub, personal, signer, parity, parity_pubsub, parity_accounts, parity_set, traces, rpc, secretstore, shh, shh_pubsub. You can also disable a specific API by putting '-' in the front, example: all,-personal. safe contains following apis: web3, net, eth, pubsub, parity, parity_pubsub, traces, rpc, shh, shh_pubsub", ARG arg_jsonrpc_hosts: (String) = "none", or |c: &Config| c.rpc.as_ref()?.hosts.as_ref().map(|vec| vec.join(",")), "--jsonrpc-hosts=[HOSTS]", @@ -483,9 +495,9 @@ usage! { "--ws-interface=[IP]", "Specify the hostname portion of the WebSockets server, IP should be an interface's IP address, or all (all interfaces) or local.", - ARG arg_ws_apis: (String) = "web3,eth,pubsub,net,parity,parity_pubsub,traces,rpc,shh,shh_pubsub", or |c: &Config| c.websockets.as_ref()?.apis.as_ref().map(|vec| vec.join(",")), + ARG arg_ws_apis: (String) = "web3,eth,pubsub,net,parity,parity_pubsub,private,traces,rpc,shh,shh_pubsub", or |c: &Config| c.websockets.as_ref()?.apis.as_ref().map(|vec| vec.join(",")), "--ws-apis=[APIS]", - "Specify the APIs available through the WebSockets interface. APIS is a comma-delimited list of API name. Possible name are web3, eth, pubsub, net, personal, parity, parity_set, traces, rpc, parity_accounts, pubsub, parity_pubsub, shh, shh_pubsub, signer, secretstore.", + "Specify the APIs available through the WebSockets interface using a comma-delimited list of API names. Possible names are: all, safe, web3, net, eth, pubsub, personal, signer, parity, parity_pubsub, parity_accounts, parity_set, traces, rpc, secretstore, shh, shh_pubsub. You can also disable a specific API by putting '-' in the front, example: all,-personal. safe contains following apis: web3, net, eth, pubsub, parity, parity_pubsub, traces, rpc, shh, shh_pubsub", ARG arg_ws_origins: (String) = "parity://*,chrome-extension://*,moz-extension://*", or |c: &Config| c.websockets.as_ref()?.origins.as_ref().map(|vec| vec.join(",")), "--ws-origins=[URL]", @@ -495,6 +507,10 @@ usage! { "--ws-hosts=[HOSTS]", "List of allowed Host header values. This option will validate the Host header sent by the browser, it is additional security against some attack vectors. Special options: \"all\", \"none\".", + ARG arg_ws_max_connections: (usize) = 100usize, or |c: &Config| c.websockets.as_ref()?.max_connections, + "--ws-max-connections=[CONN]", + "Maximal number of allowed concurrent WS connections.", + ["API and console options – IPC"] FLAG flag_no_ipc: (bool) = false, or |c: &Config| c.ipc.as_ref()?.disable.clone(), "--no-ipc", @@ -504,9 +520,9 @@ usage! { "--ipc-path=[PATH]", "Specify custom path for JSON-RPC over IPC service.", - ARG arg_ipc_apis: (String) = "web3,eth,pubsub,net,parity,parity_pubsub,parity_accounts,traces,rpc,shh,shh_pubsub", or |c: &Config| c.ipc.as_ref()?.apis.as_ref().map(|vec| vec.join(",")), + ARG arg_ipc_apis: (String) = "web3,eth,pubsub,net,parity,parity_pubsub,parity_accounts,private,traces,rpc,shh,shh_pubsub", or |c: &Config| c.ipc.as_ref()?.apis.as_ref().map(|vec| vec.join(",")), "--ipc-apis=[APIS]", - "Specify custom API set available via JSON-RPC over IPC.", + "Specify custom API set available via JSON-RPC over IPC using a comma-delimited list of API names. Possible names are: all, safe, web3, net, eth, pubsub, personal, signer, parity, parity_pubsub, parity_accounts, parity_set, traces, rpc, secretstore, shh, shh_pubsub. You can also disable a specific API by putting '-' in the front, example: all,-personal. safe contains: web3, net, eth, pubsub, parity, parity_pubsub, traces, rpc, shh, shh_pubsub", ["API and console options – Dapps"] FLAG flag_no_dapps: (bool) = false, or |c: &Config| c.dapps.as_ref()?.disable.clone(), @@ -547,22 +563,42 @@ usage! { "--no-secretstore-http", "Disable Secret Store HTTP API.", - FLAG flag_no_secretstore_acl_check: (bool) = false, or |c: &Config| c.secretstore.as_ref()?.disable_acl_check.clone(), - "--no-acl-check", - "Disable ACL check (useful for test environments).", - FLAG flag_no_secretstore_auto_migrate: (bool) = false, or |c: &Config| c.secretstore.as_ref()?.disable_auto_migrate.clone(), "--no-secretstore-auto-migrate", "Do not run servers set change session automatically when servers set changes. This option has no effect when servers set is read from configuration file.", - ARG arg_secretstore_contract: (String) = "none", or |c: &Config| c.secretstore.as_ref()?.service_contract.clone(), + ARG arg_secretstore_acl_contract: (Option) = Some("registry".into()), or |c: &Config| c.secretstore.as_ref()?.acl_contract.clone(), + "--secretstore-acl-contract=[SOURCE]", + "Secret Store permissioning contract address source: none, registry (contract address is read from 'secretstore_acl_checker' entry in registry) or address.", + + ARG arg_secretstore_contract: (Option) = None, or |c: &Config| c.secretstore.as_ref()?.service_contract.clone(), "--secretstore-contract=[SOURCE]", - "Secret Store Service contract address source: none, registry (contract address is read from registry) or address.", + "Secret Store Service contract address source: none, registry (contract address is read from 'secretstore_service' entry in registry) or address.", + + ARG arg_secretstore_srv_gen_contract: (Option) = None, or |c: &Config| c.secretstore.as_ref()?.service_contract_srv_gen.clone(), + "--secretstore-srv-gen-contract=[SOURCE]", + "Secret Store Service server key generation contract address source: none, registry (contract address is read from 'secretstore_service_srv_gen' entry in registry) or address.", + + ARG arg_secretstore_srv_retr_contract: (Option) = None, or |c: &Config| c.secretstore.as_ref()?.service_contract_srv_retr.clone(), + "--secretstore-srv-retr-contract=[SOURCE]", + "Secret Store Service server key retrieval contract address source: none, registry (contract address is read from 'secretstore_service_srv_retr' entry in registry) or address.", + + ARG arg_secretstore_doc_store_contract: (Option) = None, or |c: &Config| c.secretstore.as_ref()?.service_contract_doc_store.clone(), + "--secretstore-doc-store-contract=[SOURCE]", + "Secret Store Service document key store contract address source: none, registry (contract address is read from 'secretstore_service_doc_store' entry in registry) or address.", + + ARG arg_secretstore_doc_sretr_contract: (Option) = None, or |c: &Config| c.secretstore.as_ref()?.service_contract_doc_sretr.clone(), + "--secretstore-doc-sretr-contract=[SOURCE]", + "Secret Store Service document key shadow retrieval contract address source: none, registry (contract address is read from 'secretstore_service_doc_sretr' entry in registry) or address.", ARG arg_secretstore_nodes: (String) = "", or |c: &Config| c.secretstore.as_ref()?.nodes.as_ref().map(|vec| vec.join(",")), "--secretstore-nodes=[NODES]", "Comma-separated list of other secret store cluster nodes in form NODE_PUBLIC_KEY_IN_HEX@NODE_IP_ADDR:NODE_PORT.", + ARG arg_secretstore_server_set_contract: (Option) = Some("registry".into()), or |c: &Config| c.secretstore.as_ref()?.server_set_contract.clone(), + "--secretstore-server-set-contract=[SOURCE]", + "Secret Store server set contract address source: none, registry (contract address is read from 'secretstore_server_set' entry in registry) or address.", + ARG arg_secretstore_interface: (String) = "local", or |c: &Config| c.secretstore.as_ref()?.interface.clone(), "--secretstore-interface=[IP]", "Specify the hostname portion for listening to Secret Store Key Server internal requests, IP should be an interface's IP address, or local.", @@ -604,6 +640,10 @@ usage! { "--remove-solved", "Move solved blocks from the work package queue instead of cloning them. This gives a slightly faster import speed, but means that extra solutions submitted for the same work package will go unused.", + FLAG flag_tx_queue_no_unfamiliar_locals: (bool) = false, or |c: &Config| c.mining.as_ref()?.tx_queue_no_unfamiliar_locals.clone(), + "--tx-queue-no-unfamiliar-locals", + "Transactions recieved via local means (RPC, WS, etc) will be treated as external if the sending account is unknown.", + FLAG flag_refuse_service_transactions: (bool) = false, or |c: &Config| c.mining.as_ref()?.refuse_service_transactions.clone(), "--refuse-service-transactions", "Always refuse service transactions.", @@ -660,29 +700,25 @@ usage! { "--gas-cap=[GAS]", "A cap on how large we will raise the gas limit per block due to transaction volume.", - ARG arg_tx_queue_mem_limit: (u32) = 2u32, or |c: &Config| c.mining.as_ref()?.tx_queue_mem_limit.clone(), + ARG arg_tx_queue_mem_limit: (u32) = 4u32, or |c: &Config| c.mining.as_ref()?.tx_queue_mem_limit.clone(), "--tx-queue-mem-limit=[MB]", "Maximum amount of memory that can be used by the transaction queue. Setting this parameter to 0 disables limiting.", - ARG arg_tx_queue_size: (usize) = 8192usize, or |c: &Config| c.mining.as_ref()?.tx_queue_size.clone(), + ARG arg_tx_queue_size: (usize) = 8_192usize, or |c: &Config| c.mining.as_ref()?.tx_queue_size.clone(), "--tx-queue-size=[LIMIT]", "Maximum amount of transactions in the queue (waiting to be included in next block).", + ARG arg_tx_queue_per_sender: (Option) = None, or |c: &Config| c.mining.as_ref()?.tx_queue_per_sender.clone(), + "--tx-queue-per-sender=[LIMIT]", + "Maximum number of transactions per sender in the queue. By default it's 1% of the entire queue, but not less than 16.", + ARG arg_tx_queue_gas: (String) = "off", or |c: &Config| c.mining.as_ref()?.tx_queue_gas.clone(), "--tx-queue-gas=[LIMIT]", "Maximum amount of total gas for external transactions in the queue. LIMIT can be either an amount of gas or 'auto' or 'off'. 'auto' sets the limit to be 20x the current block gas limit.", ARG arg_tx_queue_strategy: (String) = "gas_price", or |c: &Config| c.mining.as_ref()?.tx_queue_strategy.clone(), "--tx-queue-strategy=[S]", - "Prioritization strategy used to order transactions in the queue. S may be: gas - Prioritize txs with low gas limit; gas_price - Prioritize txs with high gas price; gas_factor - Prioritize txs using gas price and gas limit ratio.", - - ARG arg_tx_queue_ban_count: (u16) = 1u16, or |c: &Config| c.mining.as_ref()?.tx_queue_ban_count.clone(), - "--tx-queue-ban-count=[C]", - "Number of times maximal time for execution (--tx-time-limit) can be exceeded before banning sender/recipient/code.", - - ARG arg_tx_queue_ban_time: (u16) = 180u16, or |c: &Config| c.mining.as_ref()?.tx_queue_ban_time.clone(), - "--tx-queue-ban-time=[SEC]", - "Banning time (in seconds) for offenders of specified execution time limit. Also number of offending actions have to reach the threshold within that time.", + "Prioritization strategy used to order transactions in the queue. S may be: gas_price - Prioritize txs with high gas price", ARG arg_stratum_interface: (String) = "local", or |c: &Config| c.stratum.as_ref()?.interface.clone(), "--stratum-interface=[IP]", @@ -700,6 +736,10 @@ usage! { "--gas-price-percentile=[PCT]", "Set PCT percentile gas price value from last 100 blocks as default gas price when sending transactions.", + ARG arg_poll_lifetime: (u32) = 60u32, or |c: &Config| c.mining.as_ref()?.poll_lifetime.clone(), + "--poll-lifetime=[S]", + "Set the lifetime of the internal index filter to S seconds.", + ARG arg_author: (Option) = None, or |c: &Config| c.mining.as_ref()?.author.clone(), "--author=[ADDRESS]", "Specify the block author (aka \"coinbase\") address for sending block rewards from sealed blocks. NOTE: MINING WILL NOT WORK WITHOUT THIS OPTION.", // Sealing/Mining Option @@ -714,7 +754,7 @@ usage! { ARG arg_tx_time_limit: (Option) = None, or |c: &Config| c.mining.as_ref()?.tx_time_limit.clone(), "--tx-time-limit=[MS]", - "Maximal time for processing single transaction. If enabled senders/recipients/code of transactions offending the limit will be banned from being included in transaction queue for 180 seconds.", + "Maximal time for processing single transaction. If enabled senders of transactions offending the limit will get other transactions penalized.", ARG arg_extra_data: (Option) = None, or |c: &Config| c.mining.as_ref()?.extra_data.clone(), "--extra-data=[STRING]", @@ -825,11 +865,6 @@ usage! { "--no-periodic-snapshot", "Disable automated snapshots which usually occur once every 10000 blocks.", - ["Virtual Machine options"] - FLAG flag_jitvm: (bool) = false, or |c: &Config| c.vm.as_ref()?.jit.clone(), - "--jitvm", - "Enable the JIT VM.", - ["Whisper options"] FLAG flag_whisper: (bool) = false, or |c: &Config| c.whisper.as_ref()?.enabled, "--whisper", @@ -842,7 +877,7 @@ usage! { ["Legacy options"] FLAG flag_warp: (bool) = false, or |_| None, "--warp", - "Does nothing; warp sync is enabled by default.", + "Does nothing; warp sync is enabled by default. Use --no-warp to disable.", FLAG flag_dapps_apis_all: (bool) = false, or |_| None, "--dapps-apis-all", @@ -892,6 +927,34 @@ usage! { "--rpc", "Does nothing; JSON-RPC is on by default now.", + FLAG flag_public_node: (bool) = false, or |_| None, + "--public-node", + "Does nothing; Public node is removed from Parity.", + + FLAG flag_force_ui: (bool) = false, or |_| None, + "--force-ui", + "Does nothing; UI is now a separate project.", + + FLAG flag_no_ui: (bool) = false, or |_| None, + "--no-ui", + "Does nothing; UI is now a separate project.", + + FLAG flag_ui_no_validation: (bool) = false, or |_| None, + "--ui-no-validation", + "Does nothing; UI is now a separate project.", + + ARG arg_ui_interface: (String) = "local", or |_| None, + "--ui-interface=[IP]", + "Does nothing; UI is now a separate project.", + + ARG arg_ui_hosts: (String) = "none", or |_| None, + "--ui-hosts=[HOSTS]", + "Does nothing; UI is now a separate project.", + + ARG arg_ui_port: (u16) = 8180u16, or |_| None, + "--ui-port=[PORT]", + "Does nothing; UI is now a separate project.", + ARG arg_dapps_port: (Option) = None, or |c: &Config| c.dapps.as_ref()?.port.clone(), "--dapps-port=[PORT]", "Dapps server is merged with RPC server. Use --jsonrpc-port.", @@ -972,6 +1035,13 @@ usage! { "--cache=[MB]", "Equivalent to --cache-size MB.", + ARG arg_tx_queue_ban_count: (u16) = 1u16, or |c: &Config| c.mining.as_ref()?.tx_queue_ban_count.clone(), + "--tx-queue-ban-count=[C]", + "Not supported.", + + ARG arg_tx_queue_ban_time: (u16) = 180u16, or |c: &Config| c.mining.as_ref()?.tx_queue_ban_time.clone(), + "--tx-queue-ban-time=[SEC]", + "Not supported.", } } @@ -987,11 +1057,11 @@ struct Config { ipc: Option, dapps: Option, secretstore: Option, + private_tx: Option, ipfs: Option, mining: Option, footprint: Option, snapshots: Option, - vm: Option, misc: Option, stratum: Option, whisper: Option, @@ -1004,8 +1074,9 @@ struct Operating { mode_timeout: Option, mode_alarm: Option, auto_update: Option, + auto_update_delay: Option, + auto_update_check_frequency: Option, release_track: Option, - public_node: Option, no_download: Option, no_consensus: Option, chain: Option, @@ -1016,6 +1087,9 @@ struct Operating { light: Option, no_persistent_txqueue: Option, no_hardcoded_sync: Option, + + #[serde(rename="public_node")] + _legacy_public_node: Option, } #[derive(Default, Debug, PartialEq, Deserialize)] @@ -1029,22 +1103,42 @@ struct Account { fast_unlock: Option, } +#[derive(Default, Debug, PartialEq, Deserialize)] +#[serde(deny_unknown_fields)] +struct PrivateTransactions { + enabled: Option, + signer: Option, + validators: Option>, + account: Option, + passwords: Option, + sstore_url: Option, + sstore_threshold: Option, +} + #[derive(Default, Debug, PartialEq, Deserialize)] #[serde(deny_unknown_fields)] struct Ui { - force: Option, - disable: Option, - port: Option, - interface: Option, - hosts: Option>, path: Option, + + #[serde(rename="force")] + _legacy_force: Option, + #[serde(rename="disable")] + _legacy_disable: Option, + #[serde(rename="port")] + _legacy_port: Option, + #[serde(rename="interface")] + _legacy_interface: Option, + #[serde(rename="hosts")] + _legacy_hosts: Option>, } #[derive(Default, Debug, PartialEq, Deserialize)] #[serde(deny_unknown_fields)] struct Network { warp: Option, + warp_barrier: Option, port: Option, + interface: Option, min_peers: Option, max_peers: Option, snapshot_peers: Option, @@ -1082,6 +1176,7 @@ struct Ws { apis: Option>, origins: Option>, hosts: Option>, + max_connections: Option, } #[derive(Default, Debug, PartialEq, Deserialize)] @@ -1110,12 +1205,17 @@ struct Dapps { struct SecretStore { disable: Option, disable_http: Option, - disable_acl_check: Option, disable_auto_migrate: Option, + acl_contract: Option, service_contract: Option, + service_contract_srv_gen: Option, + service_contract_srv_retr: Option, + service_contract_doc_store: Option, + service_contract_doc_sretr: Option, self_secret: Option, admin_public: Option, nodes: Option>, + server_set_contract: Option, interface: Option, port: Option, http_interface: Option, @@ -1149,6 +1249,7 @@ struct Mining { relay_set: Option, min_gas_price: Option, gas_price_percentile: Option, + poll_lifetime: Option, usd_per_tx: Option, usd_per_eth: Option, price_update_period: Option, @@ -1156,11 +1257,13 @@ struct Mining { gas_cap: Option, extra_data: Option, tx_queue_size: Option, + tx_queue_per_sender: Option, tx_queue_mem_limit: Option, tx_queue_gas: Option, tx_queue_strategy: Option, tx_queue_ban_count: Option, tx_queue_ban_time: Option, + tx_queue_no_unfamiliar_locals: Option, remove_solved: Option, notify_work: Option>, refuse_service_transactions: Option, @@ -1200,12 +1303,6 @@ struct Snapshots { disable_periodic: Option, } -#[derive(Default, Debug, PartialEq, Deserialize)] -#[serde(deny_unknown_fields)] -struct VM { - jit: Option, -} - #[derive(Default, Debug, PartialEq, Deserialize)] #[serde(deny_unknown_fields)] struct Misc { @@ -1229,7 +1326,7 @@ mod tests { use super::{ Args, ArgsError, Config, Operating, Account, Ui, Network, Ws, Rpc, Ipc, Dapps, Ipfs, Mining, Footprint, - Snapshots, VM, Misc, Whisper, SecretStore, + Snapshots, Misc, Whisper, SecretStore, }; use toml; use clap::{ErrorKind as ClapErrorKind}; @@ -1323,15 +1420,13 @@ mod tests { let args = Args::parse(&["parity", "--secretstore-nodes", "abc@127.0.0.1:3333,cde@10.10.10.10:4444"]).unwrap(); assert_eq!(args.arg_secretstore_nodes, "abc@127.0.0.1:3333,cde@10.10.10.10:4444"); - let args = Args::parse(&["parity", "--password", "~/.safe/1", "--password", "~/.safe/2", "--ui-port", "8123", "ui"]).unwrap(); + let args = Args::parse(&["parity", "--password", "~/.safe/1", "--password", "~/.safe/2", "--ui-port", "8123"]).unwrap(); assert_eq!(args.arg_password, vec!["~/.safe/1".to_owned(), "~/.safe/2".to_owned()]); assert_eq!(args.arg_ui_port, 8123); - assert_eq!(args.cmd_ui, true); - let args = Args::parse(&["parity", "--password", "~/.safe/1,~/.safe/2", "--ui-port", "8123", "ui"]).unwrap(); + let args = Args::parse(&["parity", "--password", "~/.safe/1,~/.safe/2", "--ui-port", "8123"]).unwrap(); assert_eq!(args.arg_password, vec!["~/.safe/1".to_owned(), "~/.safe/2".to_owned()]); assert_eq!(args.arg_ui_port, 8123); - assert_eq!(args.cmd_ui, true); } #[test] @@ -1395,7 +1490,6 @@ mod tests { // then assert_eq!(args, Args { // Commands - cmd_ui: false, cmd_dapp: false, cmd_daemon: false, cmd_account: false, @@ -1444,6 +1538,8 @@ mod tests { arg_mode_timeout: 300u64, arg_mode_alarm: 3600u64, arg_auto_update: "none".into(), + arg_auto_update_delay: 200u16, + arg_auto_update_check_frequency: 50u16, arg_release_track: "current".into(), flag_public_node: false, flag_no_download: false, @@ -1471,10 +1567,19 @@ mod tests { flag_no_hardware_wallets: false, flag_fast_unlock: false, + // -- Private Transactions Options + flag_private_enabled: true, + arg_private_signer: Some("0xdeadbeefcafe0000000000000000000000000000".into()), + arg_private_validators: Some("0xdeadbeefcafe0000000000000000000000000000".into()), + arg_private_passwords: Some("~/.safe/password.file".into()), + arg_private_account: Some("0xdeadbeefcafe0000000000000000000000000000".into()), + arg_private_sstore_url: Some("http://localhost:8082".into()), + arg_private_sstore_threshold: Some(0), + flag_force_ui: false, flag_no_ui: false, arg_ui_port: 8180u16, - arg_ui_interface: "127.0.0.1".into(), + arg_ui_interface: "local".into(), arg_ui_hosts: "none".into(), arg_ui_path: "$HOME/.parity/signer".into(), flag_ui_no_validation: false, @@ -1482,8 +1587,9 @@ mod tests { // -- Networking Options flag_no_warp: false, arg_port: 30303u16, - arg_min_peers: 25u16, - arg_max_peers: 50u16, + arg_interface: "all".into(), + arg_min_peers: Some(25u16), + arg_max_peers: Some(50u16), arg_max_pending_peers: 64u16, arg_snapshot_peers: 0u16, arg_allow_ips: "all".into(), @@ -1515,6 +1621,7 @@ mod tests { arg_ws_apis: "web3,eth,net,parity,traces,rpc,secretstore".into(), arg_ws_origins: "none".into(), arg_ws_hosts: "none".into(), + arg_ws_max_connections: 100, // IPC flag_no_ipc: false, @@ -1528,12 +1635,17 @@ mod tests { // SECRETSTORE flag_no_secretstore: false, flag_no_secretstore_http: false, - flag_no_secretstore_acl_check: false, flag_no_secretstore_auto_migrate: false, - arg_secretstore_contract: "none".into(), + arg_secretstore_acl_contract: Some("registry".into()), + arg_secretstore_contract: Some("none".into()), + arg_secretstore_srv_gen_contract: Some("none".into()), + arg_secretstore_srv_retr_contract: Some("none".into()), + arg_secretstore_doc_store_contract: Some("none".into()), + arg_secretstore_doc_sretr_contract: Some("none".into()), arg_secretstore_secret: None, arg_secretstore_admin_public: None, arg_secretstore_nodes: "".into(), + arg_secretstore_server_set_contract: Some("registry".into()), arg_secretstore_interface: "local".into(), arg_secretstore_port: 8083u16, arg_secretstore_http_interface: "local".into(), @@ -1562,13 +1674,16 @@ mod tests { arg_min_gas_price: Some(0u64), arg_usd_per_tx: "0.0001".into(), arg_gas_price_percentile: 50usize, + arg_poll_lifetime: 60u32, arg_usd_per_eth: "auto".into(), arg_price_update_period: "hourly".into(), arg_gas_floor_target: "4700000".into(), arg_gas_cap: "6283184".into(), arg_extra_data: Some("Parity".into()), + flag_tx_queue_no_unfamiliar_locals: false, arg_tx_queue_size: 8192usize, - arg_tx_queue_mem_limit: 2u32, + arg_tx_queue_per_sender: None, + arg_tx_queue_mem_limit: 4u32, arg_tx_queue_gas: "off".into(), arg_tx_queue_strategy: "gas_factor".into(), arg_tx_queue_ban_count: 1u16, @@ -1613,9 +1728,6 @@ mod tests { arg_snapshot_at: "latest".into(), flag_no_periodic_snapshot: false, - // -- Virtual Machine Options - flag_jitvm: false, - // -- Whisper options. flag_whisper: false, arg_whisper_pool_size: 20, @@ -1625,6 +1737,7 @@ mod tests { flag_geth: false, flag_testnet: false, flag_import_geth_keys: false, + arg_warp_barrier: None, arg_datadir: None, arg_networkid: None, arg_peers: None, @@ -1699,8 +1812,9 @@ mod tests { mode_timeout: Some(15u64), mode_alarm: Some(10u64), auto_update: None, + auto_update_delay: None, + auto_update_check_frequency: None, release_track: None, - public_node: None, no_download: None, no_consensus: None, chain: Some("./chain.json".into()), @@ -1711,6 +1825,7 @@ mod tests { light: None, no_hardcoded_sync: None, no_persistent_txqueue: None, + _legacy_public_node: None, }), account: Some(Account { unlock: Some(vec!["0x1".into(), "0x2".into(), "0x3".into()]), @@ -1721,16 +1836,18 @@ mod tests { fast_unlock: None, }), ui: Some(Ui { - force: None, - disable: Some(true), - port: None, - interface: None, - hosts: None, path: None, + _legacy_force: None, + _legacy_disable: Some(true), + _legacy_port: None, + _legacy_interface: None, + _legacy_hosts: None, }), network: Some(Network { warp: Some(false), + warp_barrier: None, port: None, + interface: None, min_peers: Some(10), max_peers: Some(20), max_pending_peers: Some(30), @@ -1752,6 +1869,7 @@ mod tests { apis: None, origins: Some(vec!["none".into()]), hosts: None, + max_connections: None, }), rpc: Some(Rpc { disable: Some(true), @@ -1781,18 +1899,24 @@ mod tests { secretstore: Some(SecretStore { disable: None, disable_http: None, - disable_acl_check: None, disable_auto_migrate: None, + acl_contract: None, service_contract: None, + service_contract_srv_gen: None, + service_contract_srv_retr: None, + service_contract_doc_store: None, + service_contract_doc_sretr: None, self_secret: None, admin_public: None, nodes: None, + server_set_contract: None, interface: None, port: Some(8083), http_interface: None, http_port: Some(8082), path: None, }), + private_tx: None, ipfs: Some(Ipfs { enable: Some(false), port: Some(5001), @@ -1812,17 +1936,20 @@ mod tests { relay_set: None, min_gas_price: None, gas_price_percentile: None, + poll_lifetime: None, usd_per_tx: None, usd_per_eth: None, price_update_period: Some("hourly".into()), gas_floor_target: None, gas_cap: None, tx_queue_size: Some(8192), + tx_queue_per_sender: None, tx_queue_mem_limit: None, tx_queue_gas: Some("off".into()), tx_queue_strategy: None, tx_queue_ban_count: None, tx_queue_ban_time: None, + tx_queue_no_unfamiliar_locals: None, tx_gas_limit: None, tx_time_limit: None, extra_data: None, @@ -1850,9 +1977,6 @@ mod tests { snapshots: Some(Snapshots { disable_periodic: Some(true), }), - vm: Some(VM { - jit: Some(false), - }), misc: Some(Misc { ntp_servers: Some(vec!["0.parity.pool.ntp.org:123".into()]), logging: Some("own_tx=trace".into()), @@ -1868,4 +1992,18 @@ mod tests { stratum: None, }); } + + #[test] + fn should_not_accept_min_peers_bigger_than_max_peers() { + match Args::parse(&["parity", "--max-peers=39", "--min-peers=40"]) { + Err(ArgsError::PeerConfiguration) => (), + _ => assert_eq!(false, true), + } + } + + #[test] + fn should_accept_max_peers_equal_or_bigger_than_min_peers() { + Args::parse(&["parity", "--max-peers=40", "--min-peers=40"]).unwrap(); + Args::parse(&["parity", "--max-peers=100", "--min-peers=40"]).unwrap(); + } } diff --git a/parity/cli/presets/config.mining.toml b/parity/cli/presets/config.mining.toml index f6c39bdd6df6c6adaa7f0c4047eade50e695769c..6d22a0f0878bb1c34c6d8f5f4b8bb5ac51acfe6e 100644 --- a/parity/cli/presets/config.mining.toml +++ b/parity/cli/presets/config.mining.toml @@ -19,12 +19,13 @@ 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 2048 transactions in queue. -tx_queue_size = 2048 +# Parity will keep/relay at most 8192 transactions in queue. +tx_queue_size = 8192 +tx_queue_per_sender = 128 [footprint] -# If defined will never use more then 256MB for all caches. (Overrides other cache settings). -cache_size = 256 +# If defined will never use more then 1024MB for all caches. (Overrides other cache settings). +cache_size = 1024 [misc] # Logging pattern (`=`, e.g. `own_tx=trace`). diff --git a/parity/cli/presets/mod.rs b/parity/cli/presets/mod.rs index ca1ad4559be169f8d997237928aee66ec784191d..125ab510c3cac9c23a8386366351fcbb0e412a31 100644 --- a/parity/cli/presets/mod.rs +++ b/parity/cli/presets/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -25,4 +25,4 @@ pub fn preset_config_string(arg: &str) -> Result<&'static str, Error> { "dev-insecure" => Ok(include_str!("./config.dev-insecure.toml")), _ => Err(Error::new(ErrorKind::InvalidInput, "Config doesn't match any presets [dev, mining, non-standard-ports, insecure, dev-insecure]")) } -} \ No newline at end of file +} diff --git a/parity/cli/tests/config.full.toml b/parity/cli/tests/config.full.toml index 88a91e263bcca6f4c643128d06b74a980405f2c2..6a081027f2eb1f2df17d362b20ad86a8945f6ca8 100644 --- a/parity/cli/tests/config.full.toml +++ b/parity/cli/tests/config.full.toml @@ -3,6 +3,8 @@ 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 @@ -15,12 +17,22 @@ 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 @@ -79,8 +91,13 @@ pass = "test_pass" [secretstore] disable = false disable_http = false -disable_acl_check = false +acl_contract = "registry" service_contract = "none" +service_contract_srv_gen = "none" +service_contract_srv_retr = "none" +service_contract_doc_store = "none" +service_contract_doc_sretr = "none" +server_set_contract = "registry" nodes = [] http_interface = "local" http_port = 8082 @@ -117,6 +134,7 @@ tx_queue_ban_count = 1 tx_queue_ban_time = 180 #s tx_gas_limit = "6283184" tx_time_limit = 100 #ms +tx_queue_no_unfamiliar_locals = false extra_data = "Parity" remove_solved = false notify_work = ["http://localhost:3001"] @@ -141,9 +159,6 @@ num_verifiers = 6 [snapshots] disable_periodic = false -[vm] -jit = false - [misc] logging = "own_tx=trace" log_file = "/var/log/parity.log" diff --git a/parity/cli/tests/config.toml b/parity/cli/tests/config.toml index abdf3e0c799a511fc1f392566e6ce75c9a68f1b7..245935de12e88cc5d4e294b08cb539b8656d9bcc 100644 --- a/parity/cli/tests/config.toml +++ b/parity/cli/tests/config.toml @@ -74,9 +74,6 @@ scale_verifiers = false [snapshots] disable_periodic = true -[vm] -jit = false - [misc] ntp_servers = ["0.parity.pool.ntp.org:123"] logging = "own_tx=trace" diff --git a/parity/cli/usage.rs b/parity/cli/usage.rs index 2dabc8378c10b3e8ae12601fe0461140f325a3f5..9a892c0091bd2213e69ce18fa1cff129ad2e6e72 100644 --- a/parity/cli/usage.rs +++ b/parity/cli/usage.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -161,6 +161,7 @@ macro_rules! usage { Clap(ClapError), Decode(toml::de::Error), Config(String, io::Error), + PeerConfiguration, } impl ArgsError { @@ -177,6 +178,10 @@ macro_rules! usage { println_stderr!("{}", e); process::exit(2) }, + ArgsError::PeerConfiguration => { + println_stderr!("You have supplied `min_peers` > `max_peers`"); + process::exit(2) + } } } } @@ -193,6 +198,7 @@ macro_rules! usage { } } + /// Parsed command line arguments. #[derive(Debug, PartialEq)] pub struct Args { $( @@ -312,6 +318,13 @@ macro_rules! usage { pub fn parse>(command: &[S]) -> Result { let raw_args = RawArgs::parse(command)?; + if let (Some(max_peers), Some(min_peers)) = (raw_args.arg_max_peers, raw_args.arg_min_peers) { + // Invalid configuration pattern `mix_peers` > `max_peers` + if min_peers > max_peers { + return Err(ArgsError::PeerConfiguration); + } + } + // Skip loading config file if no_config flag is specified if raw_args.flag_no_config { return Ok(raw_args.into_args(Config::default())); diff --git a/parity/configuration.rs b/parity/configuration.rs index 39c18f13891d8623da2211eacb44f89fa43461a3..9e258e4267b135ec4fa1379ad4881050dbb3212a 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -19,32 +19,31 @@ use std::io::Read; use std::net::SocketAddr; use std::path::{Path, PathBuf}; use std::collections::BTreeMap; -use std::cmp::max; -use std::str::FromStr; +use std::cmp; use cli::{Args, ArgsError}; use hash::keccak; use ethereum_types::{U256, H256, Address}; use parity_version::{version_data, version}; use bytes::Bytes; use ansi_term::Colour; -use ethsync::{NetworkConfiguration, validate_node_url, self}; +use sync::{NetworkConfiguration, validate_node_url, self}; use ethcore::ethstore::ethkey::{Secret, Public}; use ethcore::client::{VMType}; -use ethcore::miner::{MinerOptions, Banning, StratumOptions}; +use ethcore::miner::{stratum, MinerOptions}; use ethcore::verification::queue::VerifierSettings; +use miner::pool; -use rpc::{IpcConfiguration, HttpConfiguration, WsConfiguration, UiConfiguration}; -use rpc_apis::ApiSet; +use rpc::{IpcConfiguration, HttpConfiguration, WsConfiguration}; use parity_rpc::NetworkSettings; use cache::CacheConfig; -use helpers::{to_duration, to_mode, to_block_id, to_u256, to_pending_set, to_price, geth_ipc_path, parity_ipc_path, -to_bootnodes, to_addresses, to_address, to_gas_limit, to_queue_strategy}; +use helpers::{to_duration, to_mode, to_block_id, to_u256, to_pending_set, to_price, geth_ipc_path, parity_ipc_path, to_bootnodes, to_addresses, to_address, to_queue_strategy, to_queue_penalization, passwords_from_files}; use dir::helpers::{replace_home, replace_home_and_local}; use params::{ResealPolicy, AccountsConfig, GasPricerConfig, MinerExtras, SpecType}; use ethcore_logger::Config as LogConfig; use dir::{self, Directories, default_hypervisor_path, default_local_path, default_data_path}; use dapps::Configuration as DappsConfiguration; use ipfs::Configuration as IpfsConfiguration; +use ethcore_private_tx::{ProviderConfig, EncryptorConfig}; use secretstore::{NodeSecretKey, Configuration as SecretStoreConfiguration, ContractAddress as SecretStoreContractAddress}; use updater::{UpdatePolicy, UpdateFilter, ReleaseTrack}; use run::RunCmd; @@ -55,6 +54,9 @@ use account::{AccountCmd, NewAccount, ListAccounts, ImportAccounts, ImportFromGe use snapshot::{self, SnapshotCommand}; use network::{IpFilter}; +const DEFAULT_MAX_PEERS: u16 = 50; +const DEFAULT_MIN_PEERS: u16 = 25; + #[derive(Debug, PartialEq)] pub enum Cmd { Run(RunCmd), @@ -62,7 +64,7 @@ pub enum Cmd { Account(AccountCmd), ImportPresaleWallet(ImportWallet), Blockchain(BlockchainCmd), - SignerToken(WsConfiguration, UiConfiguration, LogConfig), + SignerToken(WsConfiguration, LogConfig), SignerSign { id: Option, pwfile: Option, @@ -88,25 +90,30 @@ pub struct Execute { pub cmd: Cmd, } +/// Configuration for the Parity client. #[derive(Debug, PartialEq)] pub struct Configuration { + /// Arguments to be interpreted. pub args: Args, - pub spec_name_override: Option, } impl Configuration { - pub fn parse>(command: &[S], spec_name_override: Option) -> Result { - let args = Args::parse(command)?; - + /// Parses a configuration from a list of command line arguments. + /// + /// # Example + /// + /// ``` + /// let _cfg = parity::Configuration::parse_cli(&["--light", "--chain", "koven"]).unwrap(); + /// ``` + pub fn parse_cli>(command: &[S]) -> Result { let config = Configuration { - args: args, - spec_name_override: spec_name_override, + args: Args::parse(command)?, }; Ok(config) } - pub fn into_command(self) -> Result { + pub(crate) fn into_command(self) -> Result { let dirs = self.directories(); let pruning = self.args.arg_pruning.parse()?; let pruning_history = self.args.arg_pruning_history; @@ -122,14 +129,12 @@ impl Configuration { let http_conf = self.http_config()?; let ipc_conf = self.ipc_config()?; let net_conf = self.net_config()?; - let ui_conf = self.ui_config(); let network_id = self.network_id(); let cache_config = self.cache_config(); let tracing = self.args.arg_tracing.parse()?; let fat_db = self.args.arg_fat_db.parse()?; let compaction = self.args.arg_db_compaction.parse()?; let wal = !self.args.flag_fast_and_loose; - let public_node = self.args.flag_public_node; let warp_sync = !self.args.flag_no_warp; let geth_compatibility = self.args.flag_geth; let dapps_conf = self.dapps_config(); @@ -143,9 +148,9 @@ impl Configuration { let authfile = ::signer::codes_path(&ws_conf.signer_path); if self.args.cmd_signer_new_token { - Cmd::SignerToken(ws_conf, ui_conf, logger_config.clone()) + Cmd::SignerToken(ws_conf, logger_config.clone()) } else if self.args.cmd_signer_sign { - let pwfile = self.args.arg_password.first().map(|pwfile| { + let pwfile = self.accounts_config()?.password_files.first().map(|pwfile| { PathBuf::from(pwfile) }); Cmd::SignerSign { @@ -182,7 +187,7 @@ impl Configuration { iterations: self.args.arg_keys_iterations, path: dirs.keys, spec: spec, - password_file: self.args.arg_password.first().map(|x| x.to_owned()), + password_file: self.accounts_config()?.password_files.first().map(|x| x.to_owned()), }; AccountCmd::New(new_acc) } else if self.args.cmd_account_list { @@ -216,8 +221,8 @@ impl Configuration { iterations: self.args.arg_keys_iterations, path: dirs.keys, spec: spec, - wallet_path: self.args.arg_wallet_import_path.unwrap().clone(), - password_file: self.args.arg_password.first().map(|x| x.to_owned()), + wallet_path: self.args.arg_wallet_import_path.clone().unwrap(), + password_file: self.accounts_config()?.password_files.first().map(|x| x.to_owned()), }; Cmd::ImportPresaleWallet(presale_cmd) } else if self.args.cmd_import { @@ -338,6 +343,7 @@ impl Configuration { let verifier_settings = self.verifier_settings(); let whisper_config = self.whisper_config(); + let (private_provider_conf, private_enc_conf, private_tx_enabled) = self.private_provider_config()?; let run_cmd = RunCmd { cache_config: cache_config, @@ -350,6 +356,7 @@ impl Configuration { logger_config: logger_config.clone(), miner_options: self.miner_options()?, gas_price_percentile: self.args.arg_gas_price_percentile, + poll_lifetime: self.args.arg_poll_lifetime, ntp_servers: self.ntp_servers(), ws_conf: ws_conf, http_conf: http_conf, @@ -368,15 +375,16 @@ impl Configuration { wal: wal, vm_type: vm_type, warp_sync: warp_sync, - public_node: public_node, + warp_barrier: self.args.arg_warp_barrier, geth_compatibility: geth_compatibility, net_settings: self.network_settings()?, dapps_conf: dapps_conf, ipfs_conf: ipfs_conf, - ui_conf: ui_conf, secretstore_conf: secretstore_conf, + private_provider_conf: private_provider_conf, + private_encryptor_conf: private_enc_conf, + private_tx_enabled, dapp: self.dapp_to_open()?, - ui: self.args.cmd_ui, name: self.args.arg_identity, custom_bootnodes: self.args.arg_bootnodes.is_some(), no_periodic_snapshot: self.args.flag_no_periodic_snapshot, @@ -399,20 +407,18 @@ impl Configuration { } fn vm_type(&self) -> Result { - if self.args.flag_jitvm { - VMType::jit().ok_or("Parity is built without the JIT EVM.".into()) - } else { - Ok(VMType::Interpreter) - } + Ok(VMType::Interpreter) } fn miner_extras(&self) -> Result { + let floor = to_u256(&self.args.arg_gas_floor_target)?; + let ceil = to_u256(&self.args.arg_gas_cap)?; let extras = MinerExtras { author: self.author()?, extra_data: self.extra_data()?, - gas_floor_target: to_u256(&self.args.arg_gas_floor_target)?, - gas_ceil_target: to_u256(&self.args.arg_gas_cap)?, + gas_range_target: (floor, ceil), engine_signer: self.engine_signer()?, + work_notify: self.work_notify(), }; Ok(extras) @@ -451,14 +457,12 @@ impl Configuration { LogConfig { mode: self.args.arg_logging.clone(), color: !self.args.flag_no_color && !cfg!(windows), - file: self.args.arg_log_file.clone(), + file: self.args.arg_log_file.as_ref().map(|log_file| replace_home(&self.directories().base, log_file)), } } fn chain(&self) -> Result { - let name = if let Some(ref s) = self.spec_name_override { - s.clone() - } else if self.args.flag_testnet { + let name = if self.args.flag_testnet { "testnet".to_owned() } else { self.args.arg_chain.clone() @@ -468,8 +472,9 @@ impl Configuration { } fn max_peers(&self) -> u32 { - let peers = self.args.arg_max_peers as u32; - max(self.min_peers(), peers) + self.args.arg_max_peers + .or(cmp::max(self.args.arg_min_peers, Some(DEFAULT_MAX_PEERS))) + .unwrap_or(DEFAULT_MAX_PEERS) as u32 } fn ip_filter(&self) -> Result { @@ -480,7 +485,9 @@ impl Configuration { } fn min_peers(&self) -> u32 { - self.args.arg_peers.unwrap_or(self.args.arg_min_peers) as u32 + self.args.arg_min_peers + .or(cmp::min(self.args.arg_max_peers, Some(DEFAULT_MIN_PEERS))) + .unwrap_or(DEFAULT_MIN_PEERS) as u32 } fn max_pending_peers(&self) -> u32 { @@ -500,7 +507,7 @@ impl Configuration { iterations: self.args.arg_keys_iterations, refresh_time: self.args.arg_accounts_refresh, testnet: self.args.flag_testnet, - password_files: self.args.arg_password.clone(), + password_files: self.args.arg_password.iter().map(|s| replace_home(&self.directories().base, s)).collect(), unlocked_accounts: to_addresses(&self.args.arg_unlock)?, enable_hardware_wallets: !self.args.flag_no_hardware_wallets, enable_fast_unlock: self.args.flag_fast_unlock, @@ -509,9 +516,9 @@ impl Configuration { Ok(cfg) } - fn stratum_options(&self) -> Result, String> { + fn stratum_options(&self) -> Result, String> { if self.args.flag_stratum { - Ok(Some(StratumOptions { + Ok(Some(stratum::Options { io_path: self.directories().db, listen_addr: self.stratum_interface(), port: self.args.arg_ports_shift + self.args.arg_stratum_port, @@ -529,62 +536,61 @@ impl Configuration { let reseal = self.args.arg_reseal_on_txs.parse::()?; let options = MinerOptions { - new_work_notify: self.work_notify(), force_sealing: self.args.flag_force_sealing, reseal_on_external_tx: reseal.external, reseal_on_own_tx: reseal.own, reseal_on_uncle: self.args.flag_reseal_on_uncle, - tx_gas_limit: match self.args.arg_tx_gas_limit { - Some(ref d) => to_u256(d)?, - None => U256::max_value(), - }, - tx_queue_size: self.args.arg_tx_queue_size, - tx_queue_memory_limit: if self.args.arg_tx_queue_mem_limit > 0 { - Some(self.args.arg_tx_queue_mem_limit as usize * 1024 * 1024) - } else { None }, - tx_queue_gas_limit: to_gas_limit(&self.args.arg_tx_queue_gas)?, - tx_queue_strategy: to_queue_strategy(&self.args.arg_tx_queue_strategy)?, - pending_set: to_pending_set(&self.args.arg_relay_set)?, reseal_min_period: Duration::from_millis(self.args.arg_reseal_min_period), reseal_max_period: Duration::from_millis(self.args.arg_reseal_max_period), + + pending_set: to_pending_set(&self.args.arg_relay_set)?, work_queue_size: self.args.arg_work_queue_size, enable_resubmission: !self.args.flag_remove_solved, - tx_queue_banning: match self.args.arg_tx_time_limit { - Some(limit) => Banning::Enabled { - min_offends: self.args.arg_tx_queue_ban_count, - offend_threshold: Duration::from_millis(limit), - ban_duration: Duration::from_secs(self.args.arg_tx_queue_ban_time as u64), - }, - None => Banning::Disabled, - }, - refuse_service_transactions: self.args.flag_refuse_service_transactions, infinite_pending_block: self.args.flag_infinite_pending_block, + + tx_queue_penalization: to_queue_penalization(self.args.arg_tx_time_limit)?, + tx_queue_strategy: to_queue_strategy(&self.args.arg_tx_queue_strategy)?, + tx_queue_no_unfamiliar_locals: self.args.flag_tx_queue_no_unfamiliar_locals, + refuse_service_transactions: self.args.flag_refuse_service_transactions, + + pool_limits: self.pool_limits()?, + pool_verification_options: self.pool_verification_options()?, }; Ok(options) } - fn ui_port(&self) -> u16 { - self.args.arg_ports_shift + self.args.arg_ui_port + fn pool_limits(&self) -> Result { + let max_count = self.args.arg_tx_queue_size; + + Ok(pool::Options { + max_count, + max_per_sender: self.args.arg_tx_queue_per_sender.unwrap_or_else(|| cmp::max(16, max_count / 100)), + max_mem_usage: if self.args.arg_tx_queue_mem_limit > 0 { + self.args.arg_tx_queue_mem_limit as usize * 1024 * 1024 + } else { + usize::max_value() + }, + }) } - fn ntp_servers(&self) -> Vec { - self.args.arg_ntp_servers.split(",").map(str::to_owned).collect() + fn pool_verification_options(&self) -> Result{ + Ok(pool::verifier::Options { + // NOTE min_gas_price and block_gas_limit will be overwritten right after start. + minimal_gas_price: U256::from(20_000_000) * 1_000u32, + block_gas_limit: U256::max_value(), + tx_gas_limit: match self.args.arg_tx_gas_limit { + Some(ref d) => to_u256(d)?, + None => U256::max_value(), + }, + }) } - fn ui_config(&self) -> UiConfiguration { - UiConfiguration { - enabled: self.ui_enabled(), - interface: self.ui_interface(), - port: self.ui_port(), - hosts: self.ui_hosts(), - } + fn ntp_servers(&self) -> Vec { + self.args.arg_ntp_servers.split(",").map(str::to_owned).collect() } fn dapps_config(&self) -> DappsConfiguration { - let dev_ui = if self.args.flag_ui_no_validation { vec![("127.0.0.1".to_owned(), 3000)] } else { vec![] }; - let ui_port = self.ui_port(); - DappsConfiguration { enabled: self.dapps_enabled(), dapps_path: PathBuf::from(self.directories().dapps), @@ -593,31 +599,6 @@ impl Configuration { } else { vec![] }, - extra_embed_on: { - let mut extra_embed = dev_ui.clone(); - match self.ui_hosts() { - // In case host validation is disabled allow all frame ancestors - None => { - // NOTE Chrome does not seem to support "*:" - // we use `http(s)://*:` instead. - extra_embed.push(("http://*".to_owned(), ui_port)); - extra_embed.push(("https://*".to_owned(), ui_port)); - }, - Some(hosts) => extra_embed.extend(hosts.into_iter().filter_map(|host| { - let mut it = host.split(":"); - let host = it.next(); - let port = it.next().and_then(|v| u16::from_str(v).ok()); - - match (host, port) { - (Some(host), Some(port)) => Some((host.into(), port)), - (Some(host), None) => Some((host.into(), ui_port)), - _ => None, - } - })), - } - extra_embed - }, - extra_script_src: dev_ui, } } @@ -625,11 +606,16 @@ impl Configuration { Ok(SecretStoreConfiguration { enabled: self.secretstore_enabled(), http_enabled: self.secretstore_http_enabled(), - acl_check_enabled: self.secretstore_acl_check_enabled(), auto_migrate_enabled: self.secretstore_auto_migrate_enabled(), + acl_check_contract_address: self.secretstore_acl_check_contract_address()?, service_contract_address: self.secretstore_service_contract_address()?, + service_contract_srv_gen_address: self.secretstore_service_contract_srv_gen_address()?, + service_contract_srv_retr_address: self.secretstore_service_contract_srv_retr_address()?, + service_contract_doc_store_address: self.secretstore_service_contract_doc_store_address()?, + service_contract_doc_sretr_address: self.secretstore_service_contract_doc_sretr_address()?, self_secret: self.secretstore_self_secret()?, nodes: self.secretstore_nodes()?, + key_server_set_contract_address: self.secretstore_key_server_set_contract_address()?, interface: self.secretstore_interface(), port: self.args.arg_ports_shift + self.args.arg_secretstore_port, http_interface: self.secretstore_http_interface(), @@ -680,12 +666,7 @@ impl Configuration { let usd_per_tx = to_price(&self.args.arg_usd_per_tx)?; if "auto" == self.args.arg_usd_per_eth.as_str() { - // Just a very rough estimate to avoid accepting - // ZGP transactions before the price is fetched - // if user does not want it. - let last_known_usd_per_eth = 10.0; return Ok(GasPricerConfig::Calibrated { - initial_minimum: wei_per_gas(usd_per_tx, last_known_usd_per_eth), usd_per_tx: usd_per_tx, recalibration_period: to_duration(self.args.arg_price_update_period.as_str())?, }); @@ -716,15 +697,17 @@ impl Configuration { match self.args.arg_reserved_peers { Some(ref path) => { + let path = replace_home(&self.directories().base, path); + let mut buffer = String::new(); - let mut node_file = File::open(path).map_err(|e| format!("Error opening reserved nodes file: {}", e))?; + let mut node_file = File::open(&path).map_err(|e| format!("Error opening reserved nodes file: {}", e))?; node_file.read_to_string(&mut buffer).map_err(|_| "Error reading reserved node file")?; let lines = buffer.lines().map(|s| s.trim().to_owned()).filter(|s| !s.is_empty() && !s.starts_with("#")).collect::>(); for line in &lines { match validate_node_url(line).map(Into::into) { None => continue, - Some(ethsync::ErrorKind::AddressResolve(_)) => return Err(format!("Failed to resolve hostname of a boot node: {}", line)), + Some(sync::ErrorKind::AddressResolve(_)) => return Err(format!("Failed to resolve hostname of a boot node: {}", line)), Some(_) => return Err(format!("Invalid node address format given for a boot node: {}", line)), } } @@ -737,7 +720,7 @@ impl Configuration { fn net_addresses(&self) -> Result<(SocketAddr, Option), String> { let port = self.args.arg_ports_shift + self.args.arg_port; - let listen_address = SocketAddr::new("0.0.0.0".parse().unwrap(), port); + let listen_address = SocketAddr::new(self.interface(&self.args.arg_interface).parse().unwrap(), port); let public_address = if self.args.arg_nat.starts_with("extip:") { let host = &self.args.arg_nat[6..]; let host = host.parse().map_err(|_| format!("Invalid host given with `--nat extip:{}`", host))?; @@ -773,7 +756,15 @@ impl Configuration { ret.config_path = Some(net_path.to_str().unwrap().to_owned()); ret.reserved_nodes = self.init_reserved_nodes()?; ret.allow_non_reserved = !self.args.flag_reserved_only; - ret.client_version = version(); + ret.client_version = { + let mut client_version = version(); + if !self.args.arg_identity.is_empty() { + // Insert name after the "Parity/" at the beginning of version string. + let idx = client_version.find('/').unwrap_or(client_version.len()); + client_version.insert_str(idx, &format!("/{}", self.args.arg_identity)); + } + client_version + }; Ok(ret) } @@ -836,10 +827,6 @@ impl Configuration { Some(hosts) } - fn ui_hosts(&self) -> Option> { - self.hosts(&self.args.arg_ui_hosts, &self.ui_interface()) - } - fn rpc_hosts(&self) -> Option> { self.hosts(&self.args.arg_jsonrpc_hosts, &self.rpc_interface()) } @@ -849,7 +836,7 @@ impl Configuration { } fn ws_origins(&self) -> Option> { - if self.args.flag_unsafe_expose || self.args.flag_ui_no_validation { + if self.args.flag_unsafe_expose { return None; } @@ -884,10 +871,7 @@ impl Configuration { enabled: self.rpc_enabled(), interface: self.rpc_interface(), port: self.args.arg_ports_shift + self.args.arg_rpcport.unwrap_or(self.args.arg_jsonrpc_port), - apis: match self.args.flag_public_node { - false => self.rpc_apis().parse()?, - true => self.rpc_apis().parse::()?.retain(ApiSet::PublicContext), - }, + apis: self.rpc_apis().parse()?, hosts: self.rpc_hosts(), cors: self.rpc_cors(), server_threads: match self.args.arg_jsonrpc_server_threads { @@ -901,14 +885,11 @@ impl Configuration { } fn ws_config(&self) -> Result { - let ui = self.ui_config(); let http = self.http_config()?; let support_token_api = - // never enabled for public node - !self.args.flag_public_node - // enabled when not unlocking unless the ui is forced - && (self.args.arg_unlock.is_none() || ui.enabled); + // enabled when not unlocking + self.args.arg_unlock.is_none(); let conf = WsConfiguration { enabled: self.ws_enabled(), @@ -919,13 +900,36 @@ impl Configuration { origins: self.ws_origins(), signer_path: self.directories().signer.into(), support_token_api, - ui_address: ui.address(), dapps_address: http.address(), + max_connections: self.args.arg_ws_max_connections, }; Ok(conf) } + fn private_provider_config(&self) -> Result<(ProviderConfig, EncryptorConfig, bool), String> { + let provider_conf = ProviderConfig { + validator_accounts: to_addresses(&self.args.arg_private_validators)?, + signer_account: self.args.arg_private_signer.clone().and_then(|account| to_address(Some(account)).ok()), + passwords: match self.args.arg_private_passwords.clone() { + Some(file) => passwords_from_files(&vec![file].as_slice())?, + None => Vec::new(), + }, + }; + + let encryptor_conf = EncryptorConfig { + base_url: self.args.arg_private_sstore_url.clone(), + threshold: self.args.arg_private_sstore_threshold.unwrap_or(0), + key_server_account: self.args.arg_private_account.clone().and_then(|account| to_address(Some(account)).ok()), + passwords: match self.args.arg_private_passwords.clone() { + Some(file) => passwords_from_files(&vec![file].as_slice())?, + None => Vec::new(), + }, + }; + + Ok((provider_conf, encryptor_conf, self.args.flag_private_enabled)) + } + fn network_settings(&self) -> Result { let http_conf = self.http_config()?; let net_addresses = self.net_addresses()?; @@ -959,6 +963,8 @@ impl Configuration { }, path: default_hypervisor_path(), max_size: 128 * 1024 * 1024, + max_delay: self.args.arg_auto_update_delay as u64, + frequency: self.args.arg_auto_update_check_frequency as u64, }) } @@ -1017,10 +1023,6 @@ impl Configuration { }.into() } - fn ui_interface(&self) -> String { - self.interface(&self.args.arg_ui_interface) - } - fn rpc_interface(&self) -> String { let rpc_interface = self.args.arg_rpcaddr.clone().unwrap_or(self.args.arg_jsonrpc_interface.clone()); self.interface(&rpc_interface) @@ -1108,32 +1110,36 @@ impl Configuration { !self.args.flag_no_secretstore_http && cfg!(feature = "secretstore") } - fn secretstore_acl_check_enabled(&self) -> bool { - !self.args.flag_no_secretstore_acl_check - } - fn secretstore_auto_migrate_enabled(&self) -> bool { !self.args.flag_no_secretstore_auto_migrate } + fn secretstore_acl_check_contract_address(&self) -> Result, String> { + into_secretstore_service_contract_address(self.args.arg_secretstore_acl_contract.as_ref()) + } + fn secretstore_service_contract_address(&self) -> Result, String> { - Ok(match self.args.arg_secretstore_contract.as_ref() { - "none" => None, - "registry" => Some(SecretStoreContractAddress::Registry), - a => Some(SecretStoreContractAddress::Address(a.parse().map_err(|e| format!("{}", e))?)), - }) + into_secretstore_service_contract_address(self.args.arg_secretstore_contract.as_ref()) } - fn ui_enabled(&self) -> bool { - if self.args.flag_force_ui { - return true; - } + fn secretstore_service_contract_srv_gen_address(&self) -> Result, String> { + into_secretstore_service_contract_address(self.args.arg_secretstore_srv_gen_contract.as_ref()) + } - let ui_disabled = self.args.arg_unlock.is_some() || - self.args.flag_geth || - self.args.flag_no_ui; + fn secretstore_service_contract_srv_retr_address(&self) -> Result, String> { + into_secretstore_service_contract_address(self.args.arg_secretstore_srv_retr_contract.as_ref()) + } + + fn secretstore_service_contract_doc_store_address(&self) -> Result, String> { + into_secretstore_service_contract_address(self.args.arg_secretstore_doc_store_contract.as_ref()) + } - self.args.cmd_ui && !ui_disabled && cfg!(feature = "ui-enabled") + fn secretstore_service_contract_doc_sretr_address(&self) -> Result, String> { + into_secretstore_service_contract_address(self.args.arg_secretstore_doc_sretr_contract.as_ref()) + } + + fn secretstore_key_server_set_contract_address(&self) -> Result, String> { + into_secretstore_service_contract_address(self.args.arg_secretstore_server_set_contract.as_ref()) } fn verifier_settings(&self) -> VerifierSettings { @@ -1154,6 +1160,14 @@ impl Configuration { } } +fn into_secretstore_service_contract_address(s: Option<&String>) -> Result, String> { + match s.map(String::as_str) { + None | Some("none") => Ok(None), + Some("registry") => Ok(Some(SecretStoreContractAddress::Registry)), + Some(a) => Ok(Some(SecretStoreContractAddress::Address(a.parse().map_err(|e| format!("{}", e))?))), + } +} + #[cfg(test)] mod tests { use std::io::Write; @@ -1163,7 +1177,7 @@ mod tests { use tempdir::TempDir; use ethcore::client::{VMType, BlockId}; use ethcore::miner::MinerOptions; - use miner::transaction_queue::PrioritizationStrategy; + use miner::pool::PrioritizationStrategy; use parity_rpc::NetworkSettings; use updater::{UpdatePolicy, UpdateFilter, ReleaseTrack}; @@ -1174,7 +1188,8 @@ mod tests { use helpers::{default_network_config}; use params::SpecType; use presale::ImportWallet; - use rpc::{WsConfiguration, UiConfiguration}; + use rpc::WsConfiguration; + use rpc_apis::ApiSet; use run::RunCmd; use network::{AllowIP, IpFilter}; @@ -1190,7 +1205,6 @@ mod tests { fn parse(args: &[&str]) -> Configuration { Configuration { args: Args::parse_without_config(args).unwrap(), - spec_name_override: None, } } @@ -1358,14 +1372,9 @@ mod tests { origins: Some(vec!["parity://*".into(),"chrome-extension://*".into(), "moz-extension://*".into()]), hosts: Some(vec![]), signer_path: expected.into(), - ui_address: None, dapps_address: Some("127.0.0.1:8545".into()), - support_token_api: true - }, UiConfiguration { - enabled: false, - interface: "127.0.0.1".into(), - port: 8180, - hosts: Some(vec![]), + support_token_api: true, + max_connections: 100, }, LogConfig { color: true, mode: None, @@ -1373,6 +1382,17 @@ mod tests { } )); } + #[test] + fn test_ws_max_connections() { + let args = vec!["parity", "--ws-max-connections", "1"]; + let conf = parse(&args); + + assert_eq!(conf.ws_config().unwrap(), WsConfiguration { + max_connections: 1, + ..Default::default() + }); + } + #[test] fn test_run_cmd() { let args = vec!["parity"]; @@ -1388,6 +1408,7 @@ mod tests { logger_config: Default::default(), miner_options: Default::default(), gas_price_percentile: 50, + poll_lifetime: 60, ntp_servers: vec![ "0.parity.pool.ntp.org:123".into(), "1.parity.pool.ntp.org:123".into(), @@ -1399,8 +1420,8 @@ mod tests { ipc_conf: Default::default(), net_conf: default_network_config(), network_id: None, - public_node: false, warp_sync: true, + warp_barrier: None, acc_conf: Default::default(), gas_pricer_conf: Default::default(), miner_extras: Default::default(), @@ -1411,6 +1432,8 @@ mod tests { track: ReleaseTrack::Unknown, path: default_hypervisor_path(), max_size: 128 * 1024 * 1024, + max_delay: 100, + frequency: 20, }, mode: Default::default(), tracing: Default::default(), @@ -1421,9 +1444,10 @@ mod tests { net_settings: Default::default(), dapps_conf: Default::default(), ipfs_conf: Default::default(), - ui_conf: Default::default(), secretstore_conf: Default::default(), - ui: false, + private_provider_conf: Default::default(), + private_encryptor_conf: Default::default(), + private_tx_enabled: false, dapp: None, name: "".into(), custom_bootnodes: false, @@ -1451,18 +1475,12 @@ mod tests { // when let conf0 = parse(&["parity"]); - let conf1 = parse(&["parity", "--tx-queue-strategy", "gas_factor"]); let conf2 = parse(&["parity", "--tx-queue-strategy", "gas_price"]); - let conf3 = parse(&["parity", "--tx-queue-strategy", "gas"]); // then assert_eq!(conf0.miner_options().unwrap(), mining_options); - mining_options.tx_queue_strategy = PrioritizationStrategy::GasFactorAndGasPrice; - assert_eq!(conf1.miner_options().unwrap(), mining_options); mining_options.tx_queue_strategy = PrioritizationStrategy::GasPriceOnly; assert_eq!(conf2.miner_options().unwrap(), mining_options); - mining_options.tx_queue_strategy = PrioritizationStrategy::GasAndGasPrice; - assert_eq!(conf3.miner_options().unwrap(), mining_options); } #[test] @@ -1476,8 +1494,8 @@ mod tests { fn should_parse_updater_options() { // when let conf0 = parse(&["parity", "--release-track=testing"]); - let conf1 = parse(&["parity", "--auto-update", "all", "--no-consensus"]); - let conf2 = parse(&["parity", "--no-download", "--auto-update=all", "--release-track=beta"]); + let conf1 = parse(&["parity", "--auto-update", "all", "--no-consensus", "--auto-update-delay", "300"]); + let conf2 = parse(&["parity", "--no-download", "--auto-update=all", "--release-track=beta", "--auto-update-delay=300", "--auto-update-check-frequency=100"]); let conf3 = parse(&["parity", "--auto-update=xxx"]); // then @@ -1488,6 +1506,8 @@ mod tests { track: ReleaseTrack::Testing, path: default_hypervisor_path(), max_size: 128 * 1024 * 1024, + max_delay: 100, + frequency: 20, }); assert_eq!(conf1.update_policy().unwrap(), UpdatePolicy { enable_downloading: true, @@ -1496,6 +1516,8 @@ mod tests { track: ReleaseTrack::Unknown, path: default_hypervisor_path(), max_size: 128 * 1024 * 1024, + max_delay: 300, + frequency: 20, }); assert_eq!(conf2.update_policy().unwrap(), UpdatePolicy { enable_downloading: false, @@ -1504,6 +1526,8 @@ mod tests { track: ReleaseTrack::Beta, path: default_hypervisor_path(), max_size: 128 * 1024 * 1024, + max_delay: 300, + frequency: 100, }); assert!(conf3.update_policy().is_err()); } @@ -1606,30 +1630,6 @@ mod tests { assert_eq!(conf2.ipfs_cors(), Some(vec!["http://parity.io".into(),"http://something.io".into()])); } - #[test] - fn should_disable_signer_in_geth_compat() { - // given - - // when - let conf0 = parse(&["parity", "--geth"]); - let conf1 = parse(&["parity", "--geth", "--force-ui"]); - - // then - assert_eq!(conf0.ui_enabled(), false); - assert_eq!(conf1.ui_enabled(), true); - } - - #[test] - fn should_disable_signer_when_account_is_unlocked() { - // given - - // when - let conf0 = parse(&["parity", "--unlock", "0x0"]); - - // then - assert_eq!(conf0.ui_enabled(), false); - } - #[test] fn should_parse_ui_configuration() { // given @@ -1640,59 +1640,22 @@ mod tests { let conf2 = parse(&["parity", "--ui-path=signer", "--ui-port", "3123"]); let conf3 = parse(&["parity", "--ui-path=signer", "--ui-interface", "test"]); let conf4 = parse(&["parity", "--ui-path=signer", "--force-ui"]); - let conf5 = parse(&["parity", "--ui-path=signer", "ui"]); // then assert_eq!(conf0.directories().signer, "signer".to_owned()); - assert_eq!(conf0.ui_config(), UiConfiguration { - enabled: false, - interface: "127.0.0.1".into(), - port: 8180, - hosts: Some(vec![]), - }); - assert!(conf4.ws_config().unwrap().hosts.is_some()); - assert_eq!(conf4.directories().signer, "signer".to_owned()); - assert_eq!(conf4.ui_config(), UiConfiguration { - enabled: true, - interface: "127.0.0.1".into(), - port: 8180, - hosts: Some(vec![]), - }); - assert!(conf5.ws_config().unwrap().hosts.is_some()); - assert!(conf5.ws_config().unwrap().hosts.is_some()); - assert_eq!(conf5.directories().signer, "signer".to_owned()); - assert_eq!(conf5.ui_config(), UiConfiguration { - enabled: true, - interface: "127.0.0.1".into(), - port: 8180, - hosts: Some(vec![]), - }); - assert!(conf5.ws_config().unwrap().hosts.is_some()); + + assert!(conf1.ws_config().unwrap().hosts.is_some()); + assert_eq!(conf1.ws_config().unwrap().origins, Some(vec!["parity://*".into(), "chrome-extension://*".into(), "moz-extension://*".into()])); assert_eq!(conf1.directories().signer, "signer".to_owned()); - assert_eq!(conf1.ui_config(), UiConfiguration { - enabled: false, - interface: "127.0.0.1".into(), - port: 8180, - hosts: Some(vec![]), - }); - assert_eq!(conf1.dapps_config().extra_embed_on, vec![("127.0.0.1".to_owned(), 3000)]); - assert_eq!(conf1.ws_config().unwrap().origins, None); - assert_eq!(conf2.directories().signer, "signer".to_owned()); - assert_eq!(conf2.ui_config(), UiConfiguration { - enabled: false, - interface: "127.0.0.1".into(), - port: 3123, - hosts: Some(vec![]), - }); + assert!(conf2.ws_config().unwrap().hosts.is_some()); - assert_eq!(conf3.directories().signer, "signer".to_owned()); - assert_eq!(conf3.ui_config(), UiConfiguration { - enabled: false, - interface: "test".into(), - port: 8180, - hosts: Some(vec![]), - }); + assert_eq!(conf2.directories().signer, "signer".to_owned()); + assert!(conf3.ws_config().unwrap().hosts.is_some()); + assert_eq!(conf3.directories().signer, "signer".to_owned()); + + assert!(conf4.ws_config().unwrap().hosts.is_some()); + assert_eq!(conf4.directories().signer, "signer".to_owned()); } #[test] @@ -1715,7 +1678,7 @@ mod tests { let filename = tempdir.path().join("peers"); File::create(&filename).unwrap().write_all(b" \n\t\n").unwrap(); let args = vec!["parity", "--reserved-peers", filename.to_str().unwrap()]; - let conf = Configuration::parse(&args, None).unwrap(); + let conf = Configuration::parse_cli(&args).unwrap(); assert!(conf.init_reserved_nodes().is_ok()); } @@ -1725,7 +1688,7 @@ mod tests { let filename = tempdir.path().join("peers_comments"); File::create(&filename).unwrap().write_all(b"# Sample comment\nenode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@172.0.0.1:30303\n").unwrap(); let args = vec!["parity", "--reserved-peers", filename.to_str().unwrap()]; - let conf = Configuration::parse(&args, None).unwrap(); + let conf = Configuration::parse_cli(&args).unwrap(); let reserved_nodes = conf.init_reserved_nodes(); assert!(reserved_nodes.is_ok()); assert_eq!(reserved_nodes.unwrap().len(), 1); @@ -1734,7 +1697,7 @@ mod tests { #[test] fn test_dev_preset() { let args = vec!["parity", "--config", "dev"]; - let conf = Configuration::parse(&args, None).unwrap(); + let conf = Configuration::parse_cli(&args).unwrap(); match conf.into_command().unwrap().cmd { Cmd::Run(c) => { assert_eq!(c.net_settings.chain, "dev"); @@ -1748,7 +1711,7 @@ mod tests { #[test] fn test_mining_preset() { let args = vec!["parity", "--config", "mining"]; - let conf = Configuration::parse(&args, None).unwrap(); + let conf = Configuration::parse_cli(&args).unwrap(); match conf.into_command().unwrap().cmd { Cmd::Run(c) => { assert_eq!(c.net_conf.min_peers, 50); @@ -1759,8 +1722,8 @@ mod tests { assert_eq!(c.miner_options.reseal_on_external_tx, true); assert_eq!(c.miner_options.reseal_on_own_tx, true); assert_eq!(c.miner_options.reseal_min_period, Duration::from_millis(4000)); - assert_eq!(c.miner_options.tx_queue_size, 2048); - assert_eq!(c.cache_config, CacheConfig::new_with_total_cache_size(256)); + assert_eq!(c.miner_options.pool_limits.max_count, 8192); + assert_eq!(c.cache_config, CacheConfig::new_with_total_cache_size(1024)); assert_eq!(c.logger_config.mode.unwrap(), "miner=trace,own_tx=trace"); }, _ => panic!("Should be Cmd::Run"), @@ -1770,7 +1733,7 @@ mod tests { #[test] fn test_non_standard_ports_preset() { let args = vec!["parity", "--config", "non-standard-ports"]; - let conf = Configuration::parse(&args, None).unwrap(); + let conf = Configuration::parse_cli(&args).unwrap(); match conf.into_command().unwrap().cmd { Cmd::Run(c) => { assert_eq!(c.net_settings.network_port, 30305); @@ -1783,7 +1746,7 @@ mod tests { #[test] fn test_insecure_preset() { let args = vec!["parity", "--config", "insecure"]; - let conf = Configuration::parse(&args, None).unwrap(); + let conf = Configuration::parse_cli(&args).unwrap(); match conf.into_command().unwrap().cmd { Cmd::Run(c) => { assert_eq!(c.update_policy.require_consensus, false); @@ -1803,7 +1766,7 @@ mod tests { #[test] fn test_dev_insecure_preset() { let args = vec!["parity", "--config", "dev-insecure"]; - let conf = Configuration::parse(&args, None).unwrap(); + let conf = Configuration::parse_cli(&args).unwrap(); match conf.into_command().unwrap().cmd { Cmd::Run(c) => { assert_eq!(c.net_settings.chain, "dev"); @@ -1826,7 +1789,7 @@ mod tests { #[test] fn test_override_preset() { let args = vec!["parity", "--config", "mining", "--min-peers=99"]; - let conf = Configuration::parse(&args, None).unwrap(); + let conf = Configuration::parse_cli(&args).unwrap(); match conf.into_command().unwrap().cmd { Cmd::Run(c) => { assert_eq!(c.net_conf.min_peers, 99); @@ -1835,6 +1798,19 @@ mod tests { } } + #[test] + fn test_identity_arg() { + let args = vec!["parity", "--identity", "Somebody"]; + let conf = Configuration::parse_cli(&args).unwrap(); + match conf.into_command().unwrap().cmd { + Cmd::Run(c) => { + assert_eq!(c.name, "Somebody"); + assert!(c.net_conf.client_version.starts_with("Parity/Somebody/")); + } + _ => panic!("Should be Cmd::Run"), + } + } + #[test] fn should_apply_ports_shift() { // given @@ -1849,19 +1825,16 @@ mod tests { assert_eq!(conf0.network_settings().unwrap().rpc_port, 8546); assert_eq!(conf0.http_config().unwrap().port, 8546); assert_eq!(conf0.ws_config().unwrap().port, 8547); - assert_eq!(conf0.ui_config().port, 8181); assert_eq!(conf0.secretstore_config().unwrap().port, 8084); assert_eq!(conf0.secretstore_config().unwrap().http_port, 8083); assert_eq!(conf0.ipfs_config().port, 5002); assert_eq!(conf0.stratum_options().unwrap().unwrap().port, 8009); - assert_eq!(conf1.net_addresses().unwrap().0.port(), 30304); assert_eq!(conf1.network_settings().unwrap().network_port, 30304); assert_eq!(conf1.network_settings().unwrap().rpc_port, 8545); assert_eq!(conf1.http_config().unwrap().port, 8545); assert_eq!(conf1.ws_config().unwrap().port, 8547); - assert_eq!(conf1.ui_config().port, 8181); assert_eq!(conf1.secretstore_config().unwrap().port, 8084); assert_eq!(conf1.secretstore_config().unwrap().http_port, 8083); assert_eq!(conf1.ipfs_config().port, 5002); @@ -1881,8 +1854,6 @@ mod tests { assert_eq!(&conf0.ws_config().unwrap().interface, "0.0.0.0"); assert_eq!(conf0.ws_config().unwrap().hosts, None); assert_eq!(conf0.ws_config().unwrap().origins, None); - assert_eq!(&conf0.ui_config().interface, "0.0.0.0"); - assert_eq!(conf0.ui_config().hosts, None); assert_eq!(&conf0.secretstore_config().unwrap().interface, "0.0.0.0"); assert_eq!(&conf0.secretstore_config().unwrap().http_interface, "0.0.0.0"); assert_eq!(&conf0.ipfs_config().interface, "0.0.0.0"); @@ -1945,4 +1916,56 @@ mod tests { assert_eq!(std.directories().cache, dir::helpers::replace_home_and_local(&base_path, &local_path, ::dir::CACHE_PATH)); assert_eq!(base.directories().cache, "/test/cache"); } + + #[test] + fn should_respect_only_max_peers_and_default() { + let args = vec!["parity", "--max-peers=50"]; + let conf = Configuration::parse_cli(&args).unwrap(); + match conf.into_command().unwrap().cmd { + Cmd::Run(c) => { + assert_eq!(c.net_conf.min_peers, 25); + assert_eq!(c.net_conf.max_peers, 50); + }, + _ => panic!("Should be Cmd::Run"), + } + } + + #[test] + fn should_respect_only_max_peers_less_than_default() { + let args = vec!["parity", "--max-peers=5"]; + let conf = Configuration::parse_cli(&args).unwrap(); + match conf.into_command().unwrap().cmd { + Cmd::Run(c) => { + assert_eq!(c.net_conf.min_peers, 5); + assert_eq!(c.net_conf.max_peers, 5); + }, + _ => panic!("Should be Cmd::Run"), + } + } + + #[test] + fn should_respect_only_min_peers_and_default() { + let args = vec!["parity", "--min-peers=5"]; + let conf = Configuration::parse_cli(&args).unwrap(); + match conf.into_command().unwrap().cmd { + Cmd::Run(c) => { + assert_eq!(c.net_conf.min_peers, 5); + assert_eq!(c.net_conf.max_peers, 50); + }, + _ => panic!("Should be Cmd::Run"), + } + } + + #[test] + fn should_respect_only_min_peers_and_greater_than_default() { + let args = vec!["parity", "--min-peers=500"]; + let conf = Configuration::parse_cli(&args).unwrap(); + match conf.into_command().unwrap().cmd { + Cmd::Run(c) => { + assert_eq!(c.net_conf.min_peers, 500); + assert_eq!(c.net_conf.max_peers, 500); + }, + _ => panic!("Should be Cmd::Run"), + } + } } diff --git a/parity/dapps.rs b/parity/dapps.rs index d93ae2db7cc79aefc4883071adee6c433397d0a0..ce5b3a8daa73b715d422d6c2288d12b1ca6a78aa 100644 --- a/parity/dapps.rs +++ b/parity/dapps.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -21,7 +21,7 @@ use bytes::Bytes; use dir::default_data_path; use dir::helpers::replace_home; use ethcore::client::{Client, BlockChainClient, BlockId, CallContract}; -use ethsync::LightSync; +use sync::LightSync; use futures::{Future, future, IntoFuture}; use futures_cpupool::CpuPool; use hash_fetch::fetch::Client as FetchClient; @@ -39,8 +39,6 @@ pub struct Configuration { pub enabled: bool, pub dapps_path: PathBuf, pub extra_dapps: Vec, - pub extra_embed_on: Vec<(String, u16)>, - pub extra_script_src: Vec<(String, u16)>, } impl Default for Configuration { @@ -50,8 +48,6 @@ impl Default for Configuration { enabled: true, dapps_path: replace_home(&data_dir, "$BASE/dapps").into(), extra_dapps: vec![], - extra_embed_on: vec![], - extra_script_src: vec![], } } } @@ -163,7 +159,6 @@ pub struct Dependencies { pub fetch: FetchClient, pub pool: CpuPool, pub signer: Arc, - pub ui_address: Option<(String, u16)>, } pub fn new(configuration: Configuration, deps: Dependencies) -> Result, String> { @@ -176,19 +171,6 @@ pub fn new(configuration: Configuration, deps: Dependencies) -> Result Result, String> { - if !enabled { - return Ok(None); - } - - server::ui_middleware( - deps, - rpc::DAPPS_DOMAIN, ).map(Some) } @@ -214,19 +196,10 @@ mod server { _dapps_path: PathBuf, _extra_dapps: Vec, _dapps_domain: &str, - _extra_embed_on: Vec<(String, u16)>, - _extra_script_src: Vec<(String, u16)>, ) -> Result { Err("Your Parity version has been compiled without WebApps support.".into()) } - pub fn ui_middleware( - _deps: Dependencies, - _dapps_domain: &str, - ) -> Result { - Err("Your Parity version has been compiled without UI support.".into()) - } - pub fn service(_: &Option) -> Option> { None } @@ -248,8 +221,6 @@ mod server { dapps_path: PathBuf, extra_dapps: Vec, dapps_domain: &str, - extra_embed_on: Vec<(String, u16)>, - extra_script_src: Vec<(String, u16)>, ) -> Result { let signer = deps.signer; let web_proxy_tokens = Arc::new(move |token| signer.web_proxy_access_token_domain(&token)); @@ -257,9 +228,6 @@ mod server { Ok(parity_dapps::Middleware::dapps( deps.pool, deps.node_health, - deps.ui_address, - extra_embed_on, - extra_script_src, dapps_path, extra_dapps, dapps_domain, @@ -270,20 +238,6 @@ mod server { )) } - pub fn ui_middleware( - deps: Dependencies, - dapps_domain: &str, - ) -> Result { - Ok(parity_dapps::Middleware::ui( - deps.pool, - deps.node_health, - dapps_domain, - deps.contract_client, - deps.sync_status, - deps.fetch, - )) - } - pub fn service(middleware: &Option) -> Option> { middleware.as_ref().map(|m| Arc::new(DappsServiceWrapper { endpoints: m.endpoints().clone(), diff --git a/util/hash/build.rs b/parity/db/mod.rs similarity index 70% rename from util/hash/build.rs rename to parity/db/mod.rs index eecb804f90d5014eac876df4e4fcd7845371f016..39f43fd145e03e5084d0a645d3573febb6779d8a 100644 --- a/util/hash/build.rs +++ b/parity/db/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -14,13 +14,12 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . - // build.rs +//! Database-related operations. -// Bring in a dependency on an externally maintained `cc` package which manages -// invoking the C compiler. -extern crate cc; +#[path="rocksdb/mod.rs"] +mod impls; -fn main() { - cc::Build::new().file("src/tinykeccak.c").compile("libtinykeccak.a"); -} +pub use self::impls::{open_db, open_client_db, restoration_db_handler, migrate}; +#[cfg(feature = "secretstore")] +pub use self::impls::open_secretstore_db; diff --git a/parity/db/rocksdb/helpers.rs b/parity/db/rocksdb/helpers.rs new file mode 100644 index 0000000000000000000000000000000000000000..ca685d3e86db9c12dd9a14d921dd236e242dc9ab --- /dev/null +++ b/parity/db/rocksdb/helpers.rs @@ -0,0 +1,38 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use std::path::Path; +use ethcore::db::NUM_COLUMNS; +use ethcore::client::{ClientConfig, DatabaseCompactionProfile}; +use super::kvdb_rocksdb::{CompactionProfile, DatabaseConfig}; + +pub fn compaction_profile(profile: &DatabaseCompactionProfile, db_path: &Path) -> CompactionProfile { + match profile { + &DatabaseCompactionProfile::Auto => CompactionProfile::auto(db_path), + &DatabaseCompactionProfile::SSD => CompactionProfile::ssd(), + &DatabaseCompactionProfile::HDD => CompactionProfile::hdd(), + } +} + +pub fn client_db_config(client_path: &Path, client_config: &ClientConfig) -> DatabaseConfig { + let mut client_db_config = DatabaseConfig::with_columns(NUM_COLUMNS); + + client_db_config.memory_budget = client_config.db_cache_size; + client_db_config.compaction = compaction_profile(&client_config.db_compaction, &client_path); + client_db_config.wal = client_config.db_wal; + + client_db_config +} diff --git a/parity/migration.rs b/parity/db/rocksdb/migration.rs similarity index 84% rename from parity/migration.rs rename to parity/db/rocksdb/migration.rs index bd659cba0156ac795e79100883d19a9a0698e9d2..e92a9db0356aba6f2943f1c3246c9a13e06e0aa0 100644 --- a/parity/migration.rs +++ b/parity/db/rocksdb/migration.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -18,9 +18,27 @@ use std::fs; use std::io::{Read, Write, Error as IoError, ErrorKind}; use std::path::{Path, PathBuf}; use std::fmt::{Display, Formatter, Error as FmtError}; -use migr::{self, Manager as MigrationManager, Config as MigrationConfig}; -use kvdb_rocksdb::CompactionProfile; -use migrations; +use super::migration_rocksdb::{self, Manager as MigrationManager, Config as MigrationConfig, ChangeColumns}; +use super::kvdb_rocksdb::CompactionProfile; +use ethcore::client::DatabaseCompactionProfile; + +use super::helpers; + +/// The migration from v10 to v11. +/// Adds a column for node info. +pub const TO_V11: ChangeColumns = ChangeColumns { + pre_columns: Some(6), + post_columns: Some(7), + version: 11, +}; + +/// The migration from v11 to v12. +/// Adds a column for light chain storage. +pub const TO_V12: ChangeColumns = ChangeColumns { + pre_columns: Some(7), + post_columns: Some(8), + version: 12, +}; /// Database is assumed to be at default version, when no version file is found. const DEFAULT_VERSION: u32 = 5; @@ -43,7 +61,7 @@ pub enum Error { /// Migration is not possible. MigrationImpossible, /// Internal migration error. - Internal(migr::Error), + Internal(migration_rocksdb::Error), /// Migration was completed succesfully, /// but there was a problem with io. Io(IoError), @@ -69,10 +87,10 @@ impl From for Error { } } -impl From for Error { - fn from(err: migr::Error) -> Self { +impl From for Error { + fn from(err: migration_rocksdb::Error) -> Self { match err.into() { - migr::ErrorKind::Io(e) => Error::Io(e), + migration_rocksdb::ErrorKind::Io(e) => Error::Io(e), err => Error::Internal(err.into()), } } @@ -134,8 +152,8 @@ pub fn default_migration_settings(compaction_profile: &CompactionProfile) -> Mig /// Migrations on the consolidated database. fn consolidated_database_migrations(compaction_profile: &CompactionProfile) -> Result { let mut manager = MigrationManager::new(default_migration_settings(compaction_profile)); - manager.add_migration(migrations::TO_V11).map_err(|_| Error::MigrationImpossible)?; - manager.add_migration(migrations::TO_V12).map_err(|_| Error::MigrationImpossible)?; + manager.add_migration(TO_V11).map_err(|_| Error::MigrationImpossible)?; + manager.add_migration(TO_V12).map_err(|_| Error::MigrationImpossible)?; Ok(manager) } @@ -176,7 +194,9 @@ fn exists(path: &Path) -> bool { } /// Migrates the database. -pub fn migrate(path: &Path, compaction_profile: CompactionProfile) -> Result<(), Error> { +pub fn migrate(path: &Path, compaction_profile: &DatabaseCompactionProfile) -> Result<(), Error> { + let compaction_profile = helpers::compaction_profile(&compaction_profile, path); + // read version file. let version = current_version(path)?; diff --git a/parity/db/rocksdb/mod.rs b/parity/db/rocksdb/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..7bfd28f6502690048b87f8fb9ec1696f952f2972 --- /dev/null +++ b/parity/db/rocksdb/mod.rs @@ -0,0 +1,91 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +extern crate kvdb_rocksdb; +extern crate migration_rocksdb; + +use std::sync::Arc; +use std::path::Path; +use ethcore::db::NUM_COLUMNS; +use ethcore::client::{ClientConfig, DatabaseCompactionProfile}; +use kvdb::{KeyValueDB, KeyValueDBHandler}; +use self::kvdb_rocksdb::{Database, DatabaseConfig}; + +use cache::CacheConfig; + +mod migration; +mod helpers; + +pub use self::migration::migrate; + +/// Open a secret store DB using the given secret store data path. The DB path is one level beneath the data path. +#[cfg(feature = "secretstore")] +pub fn open_secretstore_db(data_path: &str) -> Result, String> { + use std::path::PathBuf; + + let mut db_path = PathBuf::from(data_path); + db_path.push("db"); + let db_path = db_path.to_str().ok_or_else(|| "Invalid secretstore path".to_string())?; + Ok(Arc::new(Database::open_default(&db_path).map_err(|e| format!("Error opening database: {:?}", e))?)) +} + +/// Open a new client DB. +pub fn open_client_db(client_path: &Path, client_config: &ClientConfig) -> Result, String> { + let client_db_config = helpers::client_db_config(client_path, client_config); + + let client_db = Arc::new(Database::open( + &client_db_config, + &client_path.to_str().expect("DB path could not be converted to string.") + ).map_err(|e| format!("Client service database error: {:?}", e))?); + + Ok(client_db) +} + +/// Create a restoration db handler using the config generated by `client_path` and `client_config`. +pub fn restoration_db_handler(client_path: &Path, client_config: &ClientConfig) -> Box { + use kvdb::Error; + + let client_db_config = helpers::client_db_config(client_path, client_config); + + struct RestorationDBHandler { + config: DatabaseConfig, + } + + impl KeyValueDBHandler for RestorationDBHandler { + fn open(&self, db_path: &Path) -> Result, Error> { + Ok(Arc::new(Database::open(&self.config, &db_path.to_string_lossy())?)) + } + } + + Box::new(RestorationDBHandler { + config: client_db_config, + }) +} + +/// Open a new main DB. +pub fn open_db(client_path: &str, cache_config: &CacheConfig, compaction: &DatabaseCompactionProfile, wal: bool) -> Result, String> { + let db_config = DatabaseConfig { + memory_budget: Some(cache_config.blockchain() as usize * 1024 * 1024), + compaction: helpers::compaction_profile(&compaction, &Path::new(client_path)), + wal: wal, + .. DatabaseConfig::with_columns(NUM_COLUMNS) + }; + + Ok(Arc::new(Database::open( + &db_config, + client_path + ).map_err(|e| format!("Failed to open database: {}", e))?)) +} diff --git a/parity/deprecated.rs b/parity/deprecated.rs index b41475d9dbdfe7dff35cdc0ed7bdf7ec025d4f18..f3e433d1389ff6f401e0d7a869ddc48dfc9ab39f 100644 --- a/parity/deprecated.rs +++ b/parity/deprecated.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -160,4 +160,3 @@ mod tests { ]); } } - diff --git a/parity/export_hardcoded_sync.rs b/parity/export_hardcoded_sync.rs index 7a48c0592e9f37ffbdd629b3ccc5dc5c297b56de..008a5b9ecdae6a6bb6a4110b2d295584a32b19d7 100644 --- a/parity/export_hardcoded_sync.rs +++ b/parity/export_hardcoded_sync.rs @@ -1,4 +1,4 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -18,9 +18,7 @@ use std::sync::Arc; use std::time::Duration; use ethcore::client::DatabaseCompactionProfile; -use ethcore::db::NUM_COLUMNS; use ethcore::spec::{SpecParams, OptimizeFor}; -use kvdb_rocksdb::{Database, DatabaseConfig}; use light::client::fetch::Unavailable as UnavailableDataFetcher; use light::Cache as LightDataCache; @@ -29,6 +27,7 @@ use helpers::execute_upgrades; use dir::Directories; use cache::CacheConfig; use user_defaults::UserDefaults; +use db; // Number of minutes before a given gas price corpus should expire. // Light client only. @@ -66,10 +65,8 @@ pub fn execute(cmd: ExportHsyncCmd) -> Result { // select pruning algorithm let algorithm = cmd.pruning.to_algorithm(&user_defaults); - let compaction = cmd.compaction.compaction_profile(db_dirs.db_root_path().as_path()); - // execute upgrades - execute_upgrades(&cmd.dirs.base, &db_dirs, algorithm, compaction.clone())?; + execute_upgrades(&cmd.dirs.base, &db_dirs, algorithm, &cmd.compaction)?; // create dirs used by parity cmd.dirs.create_dirs(false, false, false)?; @@ -90,19 +87,10 @@ pub fn execute(cmd: ExportHsyncCmd) -> Result { config.queue.max_mem_use = cmd.cache_config.queue() as usize * 1024 * 1024; // initialize database. - let db = { - let db_config = DatabaseConfig { - memory_budget: Some(cmd.cache_config.blockchain() as usize * 1024 * 1024), - compaction: compaction, - wal: cmd.wal, - .. DatabaseConfig::with_columns(NUM_COLUMNS) - }; - - Arc::new(Database::open( - &db_config, - &db_dirs.client_path(algorithm).to_str().expect("DB path could not be converted to string.") - ).map_err(|e| format!("Error opening database: {}", e))?) - }; + let db = db::open_db(&db_dirs.client_path(algorithm).to_str().expect("DB path could not be converted to string."), + &cmd.cache_config, + &cmd.compaction, + cmd.wal)?; let service = light_client::Service::start(config, &spec, UnavailableDataFetcher, db, cache) .map_err(|e| format!("Error starting light client: {}", e))?; diff --git a/parity/helpers.rs b/parity/helpers.rs index 959dddba92db266ccc62efc37881758e0647001d..8de3728c3abf93d4b5c130b832a4497072c6bc42 100644 --- a/parity/helpers.rs +++ b/parity/helpers.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -19,17 +19,16 @@ use std::io::{Write, BufReader, BufRead}; use std::time::Duration; use std::fs::File; use ethereum_types::{U256, clean_0x, Address}; -use kvdb_rocksdb::CompactionProfile; use journaldb::Algorithm; use ethcore::client::{Mode, BlockId, VMType, DatabaseCompactionProfile, ClientConfig, VerifierType}; -use ethcore::miner::{PendingSet, GasLimit}; -use miner::transaction_queue::PrioritizationStrategy; +use ethcore::miner::{PendingSet, Penalization}; +use miner::pool::PrioritizationStrategy; use cache::CacheConfig; use dir::DatabaseDirectories; use dir::helpers::replace_home; use upgrade::{upgrade, upgrade_data_paths}; -use migration::migrate; -use ethsync::{validate_node_url, self}; +use sync::{validate_node_url, self}; +use db::migrate; use path; pub fn to_duration(s: &str) -> Result { @@ -48,11 +47,11 @@ fn to_seconds(s: &str) -> Result { "1minute" | "1 minute" | "minute" => Ok(60), "hourly" | "1hour" | "1 hour" | "hour" => Ok(60 * 60), "daily" | "1day" | "1 day" | "day" => Ok(24 * 60 * 60), - x if x.ends_with("seconds") => x[0..x.len() - 7].parse().map_err(bad), - x if x.ends_with("minutes") => x[0..x.len() - 7].parse::().map_err(bad).map(|x| x * 60), - x if x.ends_with("hours") => x[0..x.len() - 5].parse::().map_err(bad).map(|x| x * 60 * 60), - x if x.ends_with("days") => x[0..x.len() - 4].parse::().map_err(bad).map(|x| x * 24 * 60 * 60), - x => x.parse().map_err(bad), + x if x.ends_with("seconds") => x[0..x.len() - 7].trim().parse().map_err(bad), + x if x.ends_with("minutes") => x[0..x.len() - 7].trim().parse::().map_err(bad).map(|x| x * 60), + x if x.ends_with("hours") => x[0..x.len() - 5].trim().parse::().map_err(bad).map(|x| x * 60 * 60), + x if x.ends_with("days") => x[0..x.len() - 4].trim().parse::().map_err(bad).map(|x| x * 24 * 60 * 60), + x => x.trim().parse().map_err(bad), } } @@ -97,23 +96,22 @@ pub fn to_pending_set(s: &str) -> Result { } } -pub fn to_gas_limit(s: &str) -> Result { - match s { - "auto" => Ok(GasLimit::Auto), - "off" => Ok(GasLimit::None), - other => Ok(GasLimit::Fixed(to_u256(other)?)), - } -} - pub fn to_queue_strategy(s: &str) -> Result { match s { - "gas" => Ok(PrioritizationStrategy::GasAndGasPrice), "gas_price" => Ok(PrioritizationStrategy::GasPriceOnly), - "gas_factor" => Ok(PrioritizationStrategy::GasFactorAndGasPrice), other => Err(format!("Invalid queue strategy: {}", other)), } } +pub fn to_queue_penalization(time: Option) -> Result { + Ok(match time { + Some(threshold_ms) => Penalization::Enabled { + offend_threshold: Duration::from_millis(threshold_ms), + }, + None => Penalization::Disabled, + }) +} + pub fn to_address(s: Option) -> Result { match s { Some(ref a) => clean_0x(a).parse().map_err(|_| format!("Invalid address: {:?}", a)), @@ -170,7 +168,7 @@ pub fn to_bootnodes(bootnodes: &Option) -> Result, String> { Some(ref x) if !x.is_empty() => x.split(',').map(|s| { match validate_node_url(s).map(Into::into) { None => Ok(s.to_owned()), - Some(ethsync::ErrorKind::AddressResolve(_)) => Err(format!("Failed to resolve hostname of a boot node: {}", s)), + Some(sync::ErrorKind::AddressResolve(_)) => Err(format!("Failed to resolve hostname of a boot node: {}", s)), Some(_) => Err(format!("Invalid node address format given for a boot node: {}", s)), } }).collect(), @@ -180,8 +178,8 @@ pub fn to_bootnodes(bootnodes: &Option) -> Result, String> { } #[cfg(test)] -pub fn default_network_config() -> ::ethsync::NetworkConfiguration { - use ethsync::{NetworkConfiguration}; +pub fn default_network_config() -> ::sync::NetworkConfiguration { + use sync::{NetworkConfiguration}; use super::network::IpFilter; NetworkConfiguration { config_path: Some(replace_home(&::dir::default_data_path(), "$BASE/network")), @@ -259,7 +257,7 @@ pub fn execute_upgrades( base_path: &str, dirs: &DatabaseDirectories, pruning: Algorithm, - compaction_profile: CompactionProfile + compaction_profile: &DatabaseCompactionProfile ) -> Result<(), String> { upgrade_data_paths(base_path, dirs, pruning); @@ -352,6 +350,8 @@ mod tests { assert_eq!(to_duration("1day").unwrap(), Duration::from_secs(1 * 24 * 60 * 60)); assert_eq!(to_duration("2days").unwrap(), Duration::from_secs(2 * 24 *60 * 60)); assert_eq!(to_duration("15days").unwrap(), Duration::from_secs(15 * 24 * 60 * 60)); + assert_eq!(to_duration("15 days").unwrap(), Duration::from_secs(15 * 24 * 60 * 60)); + assert_eq!(to_duration("2 seconds").unwrap(), Duration::from_secs(2)); } #[test] diff --git a/parity/informant.rs b/parity/informant.rs index b834b018d6c79a2a0516df868ced7cceb307e003..1840be3badd185dd24bd237a0e86657593090785 100644 --- a/parity/informant.rs +++ b/parity/informant.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -22,18 +22,18 @@ use std::sync::{Arc}; use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering as AtomicOrdering}; use std::time::{Instant, Duration}; +use atty; use ethcore::client::{ BlockId, BlockChainClient, ChainInfo, BlockInfo, BlockChainInfo, - BlockQueueInfo, ChainNotify, ClientReport, Client, ClientIoMessage + BlockQueueInfo, ChainNotify, ChainRoute, ClientReport, Client, ClientIoMessage }; use ethcore::header::BlockNumber; use ethcore::snapshot::{RestorationStatus, SnapshotService as SS}; use ethcore::snapshot::service::Service as SnapshotService; -use ethsync::{LightSyncProvider, LightSync, SyncProvider, ManageNetwork}; +use sync::{LightSyncProvider, LightSync, SyncProvider, ManageNetwork}; use io::{TimerToken, IoContext, IoHandler}; -use isatty::{stdout_isatty}; use light::Cache as LightDataCache; -use light::client::LightChainClient; +use light::client::{LightChainClient, LightChainNotify}; use number_prefix::{binary_prefix, Standalone, Prefixed}; use parity_rpc::{is_major_importing}; use parity_rpc::informant::RpcStats; @@ -145,7 +145,8 @@ impl InformantData for FullNodeInformantData { let (importing, sync_info) = match (self.sync.as_ref(), self.net.as_ref()) { (Some(sync), Some(net)) => { let status = sync.status(); - let net_config = net.network_config(); + let num_peers_range = net.num_peers_range(); + debug_assert!(num_peers_range.end > num_peers_range.start); cache_sizes.insert("sync", status.mem_used); @@ -154,7 +155,7 @@ impl InformantData for FullNodeInformantData { last_imported_block_number: status.last_imported_block_number.unwrap_or(chain_info.best_block_number), last_imported_old_block_number: status.last_imported_old_block_number, num_peers: status.num_peers, - max_peers: status.current_max_peers(net_config.min_peers, net_config.max_peers), + max_peers: status.current_max_peers(num_peers_range.start, num_peers_range.end - 1), snapshot_sync: status.is_snapshot_syncing(), })) } @@ -278,22 +279,19 @@ impl Informant { } = full_report; let rpc_stats = self.rpc_stats.as_ref(); - - let (snapshot_sync, snapshot_current, snapshot_total) = self.snapshot.as_ref().map_or((false, 0, 0), |s| + let snapshot_sync = sync_info.as_ref().map_or(false, |s| s.snapshot_sync) && self.snapshot.as_ref().map_or(false, |s| match s.status() { - RestorationStatus::Ongoing { state_chunks, block_chunks, state_chunks_done, block_chunks_done } => - (true, state_chunks_done + block_chunks_done, state_chunks + block_chunks), - _ => (false, 0, 0), + RestorationStatus::Ongoing { .. } | RestorationStatus::Initializing { .. } => true, + _ => false, } ); - let snapshot_sync = snapshot_sync && sync_info.as_ref().map_or(false, |s| s.snapshot_sync); if !importing && !snapshot_sync && elapsed < Duration::from_secs(30) { return; } *self.last_tick.write() = Instant::now(); - let paint = |c: Style, t: String| match self.with_color && stdout_isatty() { + let paint = |c: Style, t: String| match self.with_color && atty::is(atty::Stream::Stdout) { true => format!("{}", c.paint(t)), false => t, }; @@ -306,19 +304,31 @@ impl Informant { paint(White.bold(), format!("{}", chain_info.best_block_hash)), if self.target.executes_transactions() { format!("{} blk/s {} tx/s {} Mgas/s", - paint(Yellow.bold(), format!("{:4}", (client_report.blocks_imported * 1000) as u64 / elapsed.as_milliseconds())), - paint(Yellow.bold(), format!("{:4}", (client_report.transactions_applied * 1000) as u64 / elapsed.as_milliseconds())), - paint(Yellow.bold(), format!("{:3}", (client_report.gas_processed / From::from(elapsed.as_milliseconds() * 1000)).low_u64())) + paint(Yellow.bold(), format!("{:7.2}", (client_report.blocks_imported * 1000) as f64 / elapsed.as_milliseconds() as f64)), + paint(Yellow.bold(), format!("{:6.1}", (client_report.transactions_applied * 1000) as f64 / elapsed.as_milliseconds() as f64)), + paint(Yellow.bold(), format!("{:4}", (client_report.gas_processed / From::from(elapsed.as_milliseconds() * 1000)).low_u64())) ) } else { format!("{} hdr/s", - paint(Yellow.bold(), format!("{:4}", (client_report.blocks_imported * 1000) as u64 / elapsed.as_milliseconds())) + paint(Yellow.bold(), format!("{:6.1}", (client_report.blocks_imported * 1000) as f64 / elapsed.as_milliseconds() as f64)) ) }, paint(Green.bold(), format!("{:5}", queue_info.unverified_queue_size)), paint(Green.bold(), format!("{:5}", queue_info.verified_queue_size)) ), - true => format!("Syncing snapshot {}/{}", snapshot_current, snapshot_total), + true => { + self.snapshot.as_ref().map_or(String::new(), |s| + match s.status() { + RestorationStatus::Ongoing { state_chunks, block_chunks, state_chunks_done, block_chunks_done } => { + format!("Syncing snapshot {}/{}", state_chunks_done + block_chunks_done, state_chunks + block_chunks) + }, + RestorationStatus::Initializing { chunks_done } => { + format!("Snapshot initializing ({} chunks restored)", chunks_done) + }, + _ => String::new(), + } + ) + }, }, false => String::new(), }, @@ -341,8 +351,8 @@ impl Informant { Some(ref rpc_stats) => format!( "RPC: {} conn, {} req/s, {} µs", paint(Blue.bold(), format!("{:2}", rpc_stats.sessions())), - paint(Blue.bold(), format!("{:2}", rpc_stats.requests_rate())), - paint(Blue.bold(), format!("{:3}", rpc_stats.approximated_roundtrip())), + paint(Blue.bold(), format!("{:4}", rpc_stats.requests_rate())), + paint(Blue.bold(), format!("{:4}", rpc_stats.approximated_roundtrip())), ), _ => String::new(), }, @@ -351,7 +361,7 @@ impl Informant { } impl ChainNotify for Informant { - fn new_blocks(&self, imported: Vec, _invalid: Vec, _enacted: Vec, _retracted: Vec, _sealed: Vec, _proposed: Vec, duration: u64) { + fn new_blocks(&self, imported: Vec, _invalid: Vec, _route: ChainRoute, _sealed: Vec, _proposed: Vec, duration: Duration) { let mut last_import = self.last_import.lock(); let client = &self.target.client; @@ -373,7 +383,7 @@ impl ChainNotify for Informant { Colour::White.bold().paint(format!("{}", header_view.hash())), Colour::Yellow.bold().paint(format!("{}", block.transactions_count())), Colour::Yellow.bold().paint(format!("{:.2}", header_view.gas_used().low_u64() as f32 / 1000000f32)), - Colour::Purple.bold().paint(format!("{:.2}", duration as f32 / 1000000f32)), + Colour::Purple.bold().paint(format!("{}", duration.as_milliseconds())), Colour::Blue.bold().paint(format!("{:.2}", size as f32 / 1024f32)), if skipped > 0 { format!(" + another {} block(s) containing {} tx(s)", @@ -395,11 +405,38 @@ impl ChainNotify for Informant { } } +impl LightChainNotify for Informant { + fn new_headers(&self, good: &[H256]) { + let mut last_import = self.last_import.lock(); + let client = &self.target.client; + + let importing = self.target.is_major_importing(); + let ripe = Instant::now() > *last_import + Duration::from_secs(1) && !importing; + + if ripe { + if let Some(header) = good.last().and_then(|h| client.block_header(BlockId::Hash(*h))) { + info!(target: "import", "Imported {} {} ({} Mgas){}", + Colour::White.bold().paint(format!("#{}", header.number())), + Colour::White.bold().paint(format!("{}", header.hash())), + Colour::Yellow.bold().paint(format!("{:.2}", header.gas_used().low_u64() as f32 / 1000000f32)), + if good.len() > 1 { + format!(" + another {} header(s)", + Colour::Red.bold().paint(format!("{}", good.len() - 1))) + } else { + String::new() + } + ); + *last_import = Instant::now(); + } + } + } +} + const INFO_TIMER: TimerToken = 0; impl IoHandler for Informant { fn initialize(&self, io: &IoContext) { - io.register_timer(INFO_TIMER, 5000).expect("Error registering timer"); + io.register_timer(INFO_TIMER, Duration::from_secs(5)).expect("Error registering timer"); } fn timeout(&self, _io: &IoContext, timer: TimerToken) { diff --git a/parity/ipfs.rs b/parity/ipfs.rs index ac9a4662b214a51a99f168aa042d75b4162e6b19..2cc2effca5ff3bc1a29b68fcc95184851d94db55 100644 --- a/parity/ipfs.rs +++ b/parity/ipfs.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/parity/lib.rs b/parity/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..a4f7d7963ffc6e13059690881345ade22e5e68f4 --- /dev/null +++ b/parity/lib.rs @@ -0,0 +1,259 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Ethcore client application. + +#![warn(missing_docs)] +#![cfg_attr(feature = "memory_profiling", feature(alloc_system, global_allocator, allocator_api))] + +extern crate ansi_term; +extern crate docopt; +#[macro_use] +extern crate clap; +extern crate dir; +extern crate env_logger; +extern crate futures; +extern crate futures_cpupool; +extern crate atty; +extern crate jsonrpc_core; +extern crate num_cpus; +extern crate number_prefix; +extern crate parking_lot; +extern crate regex; +extern crate rlp; +extern crate rpassword; +extern crate rustc_hex; +extern crate semver; +extern crate serde; +extern crate serde_json; +#[macro_use] +extern crate serde_derive; +extern crate toml; + +extern crate ethcore; +extern crate ethcore_bytes as bytes; +extern crate ethcore_io as io; +extern crate ethcore_light as light; +extern crate ethcore_logger; +extern crate ethcore_miner as miner; +extern crate ethcore_network as network; +extern crate ethcore_private_tx; +extern crate ethcore_service; +extern crate ethcore_sync as sync; +extern crate ethcore_transaction as transaction; +extern crate ethereum_types; +extern crate ethkey; +extern crate kvdb; +extern crate node_health; +extern crate panic_hook; +extern crate parity_hash_fetch as hash_fetch; +extern crate parity_ipfs_api; +extern crate parity_local_store as local_store; +extern crate parity_reactor; +extern crate parity_rpc; +extern crate parity_updater as updater; +extern crate parity_version; +extern crate parity_whisper; +extern crate path; +extern crate rpc_cli; +extern crate node_filter; +extern crate keccak_hash as hash; +extern crate journaldb; +extern crate registrar; + +#[macro_use] +extern crate log as rlog; + +#[cfg(feature = "secretstore")] +extern crate ethcore_secretstore; + +#[cfg(feature = "dapps")] +extern crate parity_dapps; + +#[cfg(test)] +#[macro_use] +extern crate pretty_assertions; + +#[cfg(windows)] extern crate winapi; + +#[cfg(test)] +extern crate tempdir; + +#[cfg(feature = "memory_profiling")] +extern crate alloc_system; + +mod account; +mod blockchain; +mod cache; +mod cli; +mod configuration; +mod dapps; +mod export_hardcoded_sync; +mod ipfs; +mod deprecated; +mod helpers; +mod informant; +mod light_helpers; +mod modules; +mod params; +mod presale; +mod rpc; +mod rpc_apis; +mod run; +mod secretstore; +mod signer; +mod snapshot; +mod upgrade; +mod url; +mod user_defaults; +mod whisper; +mod db; + +use std::io::BufReader; +use std::fs::File; +use hash::keccak_buffer; +use cli::Args; +use configuration::{Cmd, Execute}; +use deprecated::find_deprecated; +use ethcore_logger::setup_log; +#[cfg(feature = "memory_profiling")] +use alloc_system::System; + +pub use self::configuration::Configuration; +pub use self::run::RunningClient; + +#[cfg(feature = "memory_profiling")] +#[global_allocator] +static A: System = System; + +fn print_hash_of(maybe_file: Option) -> Result { + if let Some(file) = maybe_file { + let mut f = BufReader::new(File::open(&file).map_err(|_| "Unable to open file".to_owned())?); + let hash = keccak_buffer(&mut f).map_err(|_| "Unable to read from file".to_owned())?; + Ok(format!("{:x}", hash)) + } else { + Err("Streaming from standard input not yet supported. Specify a file.".to_owned()) + } +} + +#[cfg(feature = "deadlock_detection")] +fn run_deadlock_detection_thread() { + use std::thread; + use std::time::Duration; + use parking_lot::deadlock; + use ansi_term::Style; + + info!("Starting deadlock detection thread."); + // Create a background thread which checks for deadlocks every 10s + thread::spawn(move || { + loop { + thread::sleep(Duration::from_secs(10)); + let deadlocks = deadlock::check_deadlock(); + if deadlocks.is_empty() { + continue; + } + + warn!("{} {} detected", deadlocks.len(), Style::new().bold().paint("deadlock(s)")); + for (i, threads) in deadlocks.iter().enumerate() { + warn!("{} #{}", Style::new().bold().paint("Deadlock"), i); + for t in threads { + warn!("Thread Id {:#?}", t.thread_id()); + warn!("{:#?}", t.backtrace()); + } + } + } + }); +} + + +/// Action that Parity performed when running `start`. +pub enum ExecutionAction { + /// The execution didn't require starting a node, and thus has finished. + /// Contains the string to print on stdout, if any. + Instant(Option), + + /// The client has started running and must be shut down manually by calling `shutdown`. + /// + /// If you don't call `shutdown()`, execution will continue in the background. + Running(RunningClient), +} + +fn execute(command: Execute, on_client_rq: Cr, on_updater_rq: Rr) -> Result + where Cr: Fn(String) + 'static + Send, + Rr: Fn() + 'static + Send +{ + // TODO: move this to `main()` and expose in the C API so that users can setup logging the way + // they want + let logger = setup_log(&command.logger).expect("Logger is initialized only once; qed"); + + #[cfg(feature = "deadlock_detection")] + run_deadlock_detection_thread(); + + match command.cmd { + Cmd::Run(run_cmd) => { + if let Some(ref dapp) = run_cmd.dapp { + open_dapp(&run_cmd.dapps_conf, &run_cmd.http_conf, dapp)?; + } + + let outcome = run::execute(run_cmd, logger, on_client_rq, on_updater_rq)?; + Ok(ExecutionAction::Running(outcome)) + }, + Cmd::Version => Ok(ExecutionAction::Instant(Some(Args::print_version()))), + Cmd::Hash(maybe_file) => print_hash_of(maybe_file).map(|s| ExecutionAction::Instant(Some(s))), + Cmd::Account(account_cmd) => account::execute(account_cmd).map(|s| ExecutionAction::Instant(Some(s))), + Cmd::ImportPresaleWallet(presale_cmd) => presale::execute(presale_cmd).map(|s| ExecutionAction::Instant(Some(s))), + Cmd::Blockchain(blockchain_cmd) => blockchain::execute(blockchain_cmd).map(|_| ExecutionAction::Instant(None)), + Cmd::SignerToken(ws_conf, logger_config) => signer::execute(ws_conf, logger_config).map(|s| ExecutionAction::Instant(Some(s))), + Cmd::SignerSign { id, pwfile, port, authfile } => rpc_cli::signer_sign(id, pwfile, port, authfile).map(|s| ExecutionAction::Instant(Some(s))), + Cmd::SignerList { port, authfile } => rpc_cli::signer_list(port, authfile).map(|s| ExecutionAction::Instant(Some(s))), + Cmd::SignerReject { id, port, authfile } => rpc_cli::signer_reject(id, port, authfile).map(|s| ExecutionAction::Instant(Some(s))), + Cmd::Snapshot(snapshot_cmd) => snapshot::execute(snapshot_cmd).map(|s| ExecutionAction::Instant(Some(s))), + Cmd::ExportHardcodedSync(export_hs_cmd) => export_hardcoded_sync::execute(export_hs_cmd).map(|s| ExecutionAction::Instant(Some(s))), + } +} + +/// Starts the parity client. +/// +/// `on_client_rq` is the action to perform when the client receives an RPC request to be restarted +/// with a different chain. +/// +/// `on_updater_rq` is the action to perform when the updater has a new binary to execute. +/// +/// The first parameter is the command line arguments that you would pass when running the parity +/// binary. +/// +/// On error, returns what to print on stderr. +pub fn start(conf: Configuration, on_client_rq: Cr, on_updater_rq: Rr) -> Result + where Cr: Fn(String) + 'static + Send, + Rr: Fn() + 'static + Send +{ + let deprecated = find_deprecated(&conf.args); + for d in deprecated { + println!("{}", d); + } + + execute(conf.into_command()?, on_client_rq, on_updater_rq) +} + +fn open_dapp(dapps_conf: &dapps::Configuration, rpc_conf: &rpc::HttpConfiguration, dapp: &str) -> Result<(), String> { + if !dapps_conf.enabled { + return Err("Cannot use DAPP command with Dapps turned off.".into()) + } + + let url = format!("http://{}:{}/{}/", rpc_conf.interface, rpc_conf.port, dapp); + url::open(&url).map_err(|e| format!("{}", e))?; + Ok(()) +} diff --git a/parity/light_helpers/epoch_fetch.rs b/parity/light_helpers/epoch_fetch.rs index ff384c1310ec163b96cb83f179c6a20e3e615513..a7d8f4171fbad3dbf24445a4f7a82ef6284ca639 100644 --- a/parity/light_helpers/epoch_fetch.rs +++ b/parity/light_helpers/epoch_fetch.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -21,7 +21,7 @@ use ethcore::engines::{EthEngine, StateDependentProof}; use ethcore::header::Header; use ethcore::machine::EthereumMachine; use ethcore::receipt::Receipt; -use ethsync::LightSync; +use sync::LightSync; use futures::{future, Future}; use futures::future::Either; diff --git a/parity/light_helpers/mod.rs b/parity/light_helpers/mod.rs index 5fc9c516b455fdd8d2c67a30767edc1245b5d058..c30b62da55e8aced8a5d453b1c1c21d7151eddd0 100644 --- a/parity/light_helpers/mod.rs +++ b/parity/light_helpers/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/parity/light_helpers/queue_cull.rs b/parity/light_helpers/queue_cull.rs index 091953ad5ef9dc9cb4c66140e4303e9d01efed2f..03ec2efe74d05974c4bbe0a82e6e546dd3053bff 100644 --- a/parity/light_helpers/queue_cull.rs +++ b/parity/light_helpers/queue_cull.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ use std::sync::Arc; use std::time::Duration; use ethcore::client::ClientIoMessage; -use ethsync::LightSync; +use sync::LightSync; use io::{IoContext, IoHandler, TimerToken}; use light::client::LightChainClient; @@ -35,10 +35,10 @@ use parking_lot::RwLock; // Attepmt to cull once every 10 minutes. const TOKEN: TimerToken = 1; -const TIMEOUT_MS: u64 = 1000 * 60 * 10; +const TIMEOUT: Duration = Duration::from_secs(60 * 10); // But make each attempt last only 9 minutes -const PURGE_TIMEOUT_MS: u64 = 1000 * 60 * 9; +const PURGE_TIMEOUT: Duration = Duration::from_secs(60 * 9); /// Periodically culls the transaction queue of mined transactions. pub struct QueueCull { @@ -56,7 +56,7 @@ pub struct QueueCull { impl IoHandler for QueueCull { fn initialize(&self, io: &IoContext) { - io.register_timer(TOKEN, TIMEOUT_MS).expect("Error registering timer"); + io.register_timer(TOKEN, TIMEOUT).expect("Error registering timer"); } fn timeout(&self, _io: &IoContext, timer: TimerToken) { @@ -70,7 +70,7 @@ impl IoHandler for QueueCull let start_nonce = self.client.engine().account_start_nonce(best_header.number()); info!(target: "cull", "Attempting to cull queued transactions from {} senders.", senders.len()); - self.remote.spawn_with_timeout(move || { + self.remote.spawn_with_timeout(move |_| { let maybe_fetching = sync.with_context(move |ctx| { // fetch the nonce of each sender in the queue. let nonce_reqs = senders.iter() @@ -100,6 +100,6 @@ impl IoHandler for QueueCull future::Either::B(future::ok(())) }, } - }, Duration::from_millis(PURGE_TIMEOUT_MS), || {}) + }, PURGE_TIMEOUT, || {}) } } diff --git a/parity/main.rs b/parity/main.rs index 30c3cf0e9863ebf0e9b239a2e77c8af849e63718..06671cbfebfcb263d37c5883b776b6d2851ad9d1 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -18,190 +18,28 @@ #![warn(missing_docs)] -extern crate ansi_term; -extern crate app_dirs; +extern crate parity; + extern crate ctrlc; -extern crate docopt; -#[macro_use] -extern crate clap; extern crate dir; -extern crate env_logger; extern crate fdlimit; -extern crate futures; -extern crate futures_cpupool; -extern crate isatty; -extern crate jsonrpc_core; -extern crate num_cpus; -extern crate number_prefix; -extern crate parking_lot; -extern crate regex; -extern crate rlp; -extern crate rpassword; -extern crate rustc_hex; -extern crate semver; -extern crate serde; -extern crate serde_json; #[macro_use] -extern crate serde_derive; -extern crate toml; - -extern crate ethcore; -extern crate ethcore_bytes as bytes; -extern crate ethcore_io as io; -extern crate ethcore_light as light; -extern crate ethcore_logger; -extern crate ethcore_migrations as migrations; -extern crate ethcore_miner as miner; -extern crate ethcore_network as network; -extern crate ethcore_service; -extern crate ethcore_transaction as transaction; -extern crate ethereum_types; -extern crate migration as migr; -extern crate kvdb; -extern crate kvdb_rocksdb; -extern crate ethkey; -extern crate ethsync; -extern crate node_health; +extern crate log; extern crate panic_hook; -extern crate parity_hash_fetch as hash_fetch; -extern crate parity_ipfs_api; -extern crate parity_local_store as local_store; -extern crate parity_reactor; -extern crate parity_rpc; -extern crate parity_updater as updater; -extern crate parity_version; -extern crate parity_whisper; -extern crate path; -extern crate rpc_cli; -extern crate node_filter; -extern crate keccak_hash as hash; -extern crate journaldb; -extern crate registrar; - -#[macro_use] -extern crate log as rlog; - -#[cfg(feature="stratum")] -extern crate ethcore_stratum; - -#[cfg(feature="secretstore")] -extern crate ethcore_secretstore; - -#[cfg(feature = "dapps")] -extern crate parity_dapps; - -#[cfg(test)] -#[macro_use] -extern crate pretty_assertions; +extern crate parking_lot; -#[cfg(windows)] extern crate ws2_32; #[cfg(windows)] extern crate winapi; -#[cfg(test)] -extern crate tempdir; - -mod account; -mod blockchain; -mod cache; -mod cli; -mod configuration; -mod dapps; -mod export_hardcoded_sync; -mod ipfs; -mod deprecated; -mod helpers; -mod informant; -mod light_helpers; -mod migration; -mod modules; -mod params; -mod presale; -mod rpc; -mod rpc_apis; -mod run; -mod secretstore; -mod signer; -mod snapshot; -mod upgrade; -mod url; -mod user_defaults; -mod whisper; - -#[cfg(feature="stratum")] -mod stratum; - use std::{process, env}; -use std::collections::HashMap; -use std::io::{self as stdio, BufReader, Read, Write}; +use std::io::{self as stdio, Read, Write}; use std::fs::{remove_file, metadata, File, create_dir_all}; use std::path::PathBuf; -use hash::keccak_buffer; -use cli::Args; -use configuration::{Cmd, Execute, Configuration}; -use deprecated::find_deprecated; -use ethcore_logger::setup_log; +use std::sync::Arc; +use ctrlc::CtrlC; use dir::default_hypervisor_path; - -fn print_hash_of(maybe_file: Option) -> Result { - if let Some(file) = maybe_file { - let mut f = BufReader::new(File::open(&file).map_err(|_| "Unable to open file".to_owned())?); - let hash = keccak_buffer(&mut f).map_err(|_| "Unable to read from file".to_owned())?; - Ok(format!("{:x}", hash)) - } else { - Err("Streaming from standard input not yet supported. Specify a file.".to_owned()) - } -} - -enum PostExecutionAction { - Print(String), - Restart(Option), - Quit, -} - -fn execute(command: Execute, can_restart: bool) -> Result { - let logger = setup_log(&command.logger).expect("Logger is initialized only once; qed"); - - match command.cmd { - Cmd::Run(run_cmd) => { - let (restart, spec_name) = run::execute(run_cmd, can_restart, logger)?; - Ok(if restart { PostExecutionAction::Restart(spec_name) } else { PostExecutionAction::Quit }) - }, - Cmd::Version => Ok(PostExecutionAction::Print(Args::print_version())), - Cmd::Hash(maybe_file) => print_hash_of(maybe_file).map(|s| PostExecutionAction::Print(s)), - Cmd::Account(account_cmd) => account::execute(account_cmd).map(|s| PostExecutionAction::Print(s)), - Cmd::ImportPresaleWallet(presale_cmd) => presale::execute(presale_cmd).map(|s| PostExecutionAction::Print(s)), - Cmd::Blockchain(blockchain_cmd) => blockchain::execute(blockchain_cmd).map(|_| PostExecutionAction::Quit), - Cmd::SignerToken(ws_conf, ui_conf, logger_config) => signer::execute(ws_conf, ui_conf, logger_config).map(|s| PostExecutionAction::Print(s)), - Cmd::SignerSign { id, pwfile, port, authfile } => rpc_cli::signer_sign(id, pwfile, port, authfile).map(|s| PostExecutionAction::Print(s)), - Cmd::SignerList { port, authfile } => rpc_cli::signer_list(port, authfile).map(|s| PostExecutionAction::Print(s)), - Cmd::SignerReject { id, port, authfile } => rpc_cli::signer_reject(id, port, authfile).map(|s| PostExecutionAction::Print(s)), - Cmd::Snapshot(snapshot_cmd) => snapshot::execute(snapshot_cmd).map(|s| PostExecutionAction::Print(s)), - Cmd::ExportHardcodedSync(export_hs_cmd) => export_hardcoded_sync::execute(export_hs_cmd).map(|s| PostExecutionAction::Print(s)), - } -} - -fn start(can_restart: bool) -> Result { - let args: Vec = env::args().collect(); - let conf = Configuration::parse(&args, take_spec_name_override()).unwrap_or_else(|e| e.exit()); - - let deprecated = find_deprecated(&conf.args); - for d in deprecated { - println!("{}", d); - } - - let cmd = conf.into_command()?; - execute(cmd, can_restart) -} - -#[cfg(not(feature="stratum"))] -fn stratum_main(_: &mut HashMap) {} - -#[cfg(feature="stratum")] -fn stratum_main(alt_mains: &mut HashMap) { - alt_mains.insert("stratum".to_owned(), stratum::main); -} - -fn sync_main(_: &mut HashMap) {} +use fdlimit::raise_fd_limit; +use parity::{start, ExecutionAction}; +use parking_lot::{Condvar, Mutex}; fn updates_path(name: &str) -> PathBuf { let mut dest = PathBuf::from(default_hypervisor_path()); @@ -216,15 +54,15 @@ fn latest_exe_path() -> Option { fn set_spec_name_override(spec_name: String) { if let Err(e) = create_dir_all(default_hypervisor_path()) - .and_then(|_| File::create(updates_path("spec_name_overide")) + .and_then(|_| File::create(updates_path("spec_name_override")) .and_then(|mut f| f.write_all(spec_name.as_bytes()))) { - warn!("Couldn't override chain spec: {} at {:?}", e, updates_path("spec_name_overide")); + warn!("Couldn't override chain spec: {} at {:?}", e, updates_path("spec_name_override")); } } fn take_spec_name_override() -> Option { - let p = updates_path("spec_name_overide"); + let p = updates_path("spec_name_override"); let r = File::open(p.clone()).ok() .and_then(|mut f| { let mut spec_name = String::new(); f.read_to_string(&mut spec_name).ok().map(|_| spec_name) }); let _ = remove_file(p); @@ -233,11 +71,11 @@ fn take_spec_name_override() -> Option { #[cfg(windows)] fn global_cleanup() { - // We need to cleanup all sockets before spawning another Parity process. This makes shure everything is cleaned up. - // The loop is required because of internal refernce counter for winsock dll. We don't know how many crates we use do + // We need to cleanup all sockets before spawning another Parity process. This makes sure everything is cleaned up. + // The loop is required because of internal reference counter for winsock dll. We don't know how many crates we use do // initialize it. There's at least 2 now. for _ in 0.. 10 { - unsafe { ::ws2_32::WSACleanup(); } + unsafe { ::winapi::um::winsock2::WSACleanup(); } } } @@ -249,8 +87,8 @@ fn global_init() { // When restarting in the same process this reinits windows sockets. unsafe { const WS_VERSION: u16 = 0x202; - let mut wsdata: ::winapi::winsock2::WSADATA = ::std::mem::zeroed(); - ::ws2_32::WSAStartup(WS_VERSION, &mut wsdata); + let mut wsdata: ::winapi::um::winsock2::WSADATA = ::std::mem::zeroed(); + ::winapi::um::winsock2::WSAStartup(WS_VERSION, &mut wsdata); } } @@ -276,32 +114,70 @@ const PLEASE_RESTART_EXIT_CODE: i32 = 69; // Run our version of parity. // Returns the exit error code. -fn main_direct(can_restart: bool) -> i32 { +fn main_direct(force_can_restart: bool) -> i32 { global_init(); - let mut alt_mains = HashMap::new(); - sync_main(&mut alt_mains); - stratum_main(&mut alt_mains); - let res = if let Some(f) = std::env::args().nth(1).and_then(|arg| alt_mains.get(&arg.to_string())) { - f(); - 0 + + let mut conf = { + let args = std::env::args().collect::>(); + parity::Configuration::parse_cli(&args).unwrap_or_else(|e| e.exit()) + }; + + if let Some(spec_override) = take_spec_name_override() { + conf.args.flag_testnet = false; + conf.args.arg_chain = spec_override; + } + + let can_restart = force_can_restart || conf.args.flag_can_restart; + + // increase max number of open files + raise_fd_limit(); + + let exit = Arc::new((Mutex::new((false, None)), Condvar::new())); + + let exec = if can_restart { + let e1 = exit.clone(); + let e2 = exit.clone(); + start(conf, + move |new_chain: String| { *e1.0.lock() = (true, Some(new_chain)); e1.1.notify_all(); }, + move || { *e2.0.lock() = (true, None); e2.1.notify_all(); }) } else { - match start(can_restart) { - Ok(result) => match result { - PostExecutionAction::Print(s) => { println!("{}", s); 0 }, - PostExecutionAction::Restart(spec_name_override) => { - if let Some(spec_name) = spec_name_override { - set_spec_name_override(spec_name); - } - PLEASE_RESTART_EXIT_CODE - }, - PostExecutionAction::Quit => 0, - }, - Err(err) => { - writeln!(&mut stdio::stderr(), "{}", err).expect("StdErr available; qed"); - 1 + trace!(target: "mode", "Not hypervised: not setting exit handlers."); + start(conf, move |_| {}, move || {}) + }; + + let res = match exec { + Ok(result) => match result { + ExecutionAction::Instant(Some(s)) => { println!("{}", s); 0 }, + ExecutionAction::Instant(None) => 0, + ExecutionAction::Running(client) => { + CtrlC::set_handler({ + let e = exit.clone(); + move || { e.1.notify_all(); } + }); + + // Wait for signal + let mut lock = exit.0.lock(); + let _ = exit.1.wait(&mut lock); + + client.shutdown(); + + match &*lock { + &(true, ref spec_name_override) => { + if let &Some(ref spec_name) = spec_name_override { + set_spec_name_override(spec_name.clone()); + } + PLEASE_RESTART_EXIT_CODE + }, + _ => 0, + } }, - } + }, + Err(err) => { + writeln!(&mut stdio::stderr(), "{}", err).expect("StdErr available; qed"); + 1 + }, }; + global_cleanup(); res } @@ -365,7 +241,6 @@ fn main() { } else { trace_main!("Running direct"); // Otherwise, we're presumably running the version we want. Just run and fall-through. - let can_restart = std::env::args().any(|arg| arg == "--can-restart"); - process::exit(main_direct(can_restart)); + process::exit(main_direct(false)); } } diff --git a/parity/modules.rs b/parity/modules.rs index 36cffe2ec6be1fdbf44a4050c617ecee4c6d4d70..e12e8ee458351c48398b5284aaa53fedd1c5524d 100644 --- a/parity/modules.rs +++ b/parity/modules.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -17,11 +17,11 @@ use std::sync::Arc; use ethcore::client::BlockChainClient; -use ethsync::{self, AttachedProtocol, SyncConfig, NetworkConfiguration, Params, ConnectionFilter}; +use sync::{self, AttachedProtocol, SyncConfig, NetworkConfiguration, Params, ConnectionFilter}; use ethcore::snapshot::SnapshotService; use light::Provider; -pub use ethsync::{EthSync, SyncProvider, ManageNetwork}; +pub use sync::{EthSync, SyncProvider, ManageNetwork, PrivateTxHandler}; pub use ethcore::client::ChainNotify; use ethcore_logger::Config as LogConfig; @@ -32,16 +32,18 @@ pub fn sync( net_cfg: NetworkConfiguration, client: Arc, snapshot_service: Arc, + private_tx_handler: Arc, provider: Arc, _log_settings: &LogConfig, attached_protos: Vec, connection_filter: Option>, -) -> Result { +) -> Result { let eth_sync = EthSync::new(Params { config: sync_cfg, chain: client, provider: provider, snapshot_service: snapshot_service, + private_tx_handler, network_config: net_cfg, attached_protos: attached_protos, }, diff --git a/parity/params.rs b/parity/params.rs index 4b5cd9409377c1785ae9eb6c67819f0dfcb3278c..9417c4322641a12b2a4b5ab5ef740b5d79b9d960 100644 --- a/parity/params.rs +++ b/parity/params.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -16,15 +16,16 @@ use std::{str, fs, fmt}; use std::time::Duration; + +use ethcore::client::Mode; +use ethcore::ethereum; +use ethcore::spec::{Spec, SpecParams}; use ethereum_types::{U256, Address}; use futures_cpupool::CpuPool; -use parity_version::version_data; -use journaldb::Algorithm; -use ethcore::spec::{Spec, SpecParams}; -use ethcore::ethereum; -use ethcore::client::Mode; -use ethcore::miner::{GasPricer, GasPriceCalibratorOptions}; use hash_fetch::fetch::Client as FetchClient; +use journaldb::Algorithm; +use miner::gas_pricer::{GasPricer, GasPriceCalibratorOptions}; +use parity_version::version_data; use user_defaults::UserDefaults; #[derive(Debug, PartialEq)] @@ -32,12 +33,15 @@ pub enum SpecType { Foundation, Morden, Ropsten, + Tobalaba, Kovan, Olympic, Classic, Expanse, Musicoin, Ellaism, + Easthub, + Social, Dev, Custom(String), } @@ -58,10 +62,13 @@ impl str::FromStr for SpecType { "morden" | "classic-testnet" => SpecType::Morden, "ropsten" => SpecType::Ropsten, "kovan" | "testnet" => SpecType::Kovan, + "tobalaba" => SpecType::Tobalaba, "olympic" => SpecType::Olympic, "expanse" => SpecType::Expanse, "musicoin" => SpecType::Musicoin, "ellaism" => SpecType::Ellaism, + "easthub" => SpecType::Easthub, + "social" => SpecType::Social, "dev" => SpecType::Dev, other => SpecType::Custom(other.into()), }; @@ -80,7 +87,10 @@ impl fmt::Display for SpecType { SpecType::Expanse => "expanse", SpecType::Musicoin => "musicoin", SpecType::Ellaism => "ellaism", + SpecType::Easthub => "easthub", + SpecType::Social => "social", SpecType::Kovan => "kovan", + SpecType::Tobalaba => "tobalaba", SpecType::Dev => "dev", SpecType::Custom(ref custom) => custom, }) @@ -99,6 +109,9 @@ impl SpecType { SpecType::Expanse => Ok(ethereum::new_expanse(params)), SpecType::Musicoin => Ok(ethereum::new_musicoin(params)), SpecType::Ellaism => Ok(ethereum::new_ellaism(params)), + SpecType::Easthub => Ok(ethereum::new_easthub(params)), + SpecType::Social => Ok(ethereum::new_social(params)), + SpecType::Tobalaba => Ok(ethereum::new_tobalaba(params)), SpecType::Kovan => Ok(ethereum::new_kovan(params)), SpecType::Dev => Ok(Spec::new_instant()), SpecType::Custom(ref filename) => { @@ -215,25 +228,14 @@ impl Default for AccountsConfig { pub enum GasPricerConfig { Fixed(U256), Calibrated { - initial_minimum: U256, usd_per_tx: f32, recalibration_period: Duration, } } -impl GasPricerConfig { - pub fn initial_min(&self) -> U256 { - match *self { - GasPricerConfig::Fixed(ref min) => min.clone(), - GasPricerConfig::Calibrated { ref initial_minimum, .. } => initial_minimum.clone(), - } - } -} - impl Default for GasPricerConfig { fn default() -> Self { GasPricerConfig::Calibrated { - initial_minimum: 476190464u64.into(), usd_per_tx: 0.0001f32, recalibration_period: Duration::from_secs(3600), } @@ -261,20 +263,20 @@ impl GasPricerConfig { #[derive(Debug, PartialEq)] pub struct MinerExtras { pub author: Address, - pub extra_data: Vec, - pub gas_floor_target: U256, - pub gas_ceil_target: U256, pub engine_signer: Address, + pub extra_data: Vec, + pub gas_range_target: (U256, U256), + pub work_notify: Vec, } impl Default for MinerExtras { fn default() -> Self { MinerExtras { author: Default::default(), - extra_data: version_data(), - gas_floor_target: U256::from(4_700_000), - gas_ceil_target: U256::from(6_283_184), engine_signer: Default::default(), + extra_data: version_data(), + gas_range_target: (4_700_000.into(), 6_283_184.into()), + work_notify: Default::default(), } } } diff --git a/parity/presale.rs b/parity/presale.rs index 216ff66a848b5b8185f9ba48853bfbe34ccb61dd..4106ad8992c59eda98cae51d7c289e02633455d6 100644 --- a/parity/presale.rs +++ b/parity/presale.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/parity/rpc.rs b/parity/rpc.rs index 41b10ba8725e835287c6133e50b7b4d1ad5f2cc3..3e71cc6295b93e16b866a28cc25df905e0c50c14 100644 --- a/parity/rpc.rs +++ b/parity/rpc.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -68,56 +68,6 @@ impl Default for HttpConfiguration { } } -#[derive(Debug, PartialEq, Clone)] -pub struct UiConfiguration { - pub enabled: bool, - pub interface: String, - pub port: u16, - pub hosts: Option>, -} - -impl UiConfiguration { - pub fn address(&self) -> Option { - address(self.enabled, &self.interface, self.port, &self.hosts) - } - - pub fn redirection_address(&self) -> Option<(String, u16)> { - self.address().map(|host| { - let mut it = host.split(':'); - let hostname: Option = it.next().map(|s| s.to_owned()); - let port: Option = it.next().and_then(|s| s.parse().ok()); - - (hostname.unwrap_or_else(|| "localhost".into()), port.unwrap_or(8180)) - }) - } -} - -impl From for HttpConfiguration { - fn from(conf: UiConfiguration) -> Self { - HttpConfiguration { - enabled: conf.enabled, - interface: conf.interface, - port: conf.port, - apis: rpc_apis::ApiSet::UnsafeContext, - cors: Some(vec![]), - hosts: conf.hosts, - server_threads: 1, - processing_threads: 0, - } - } -} - -impl Default for UiConfiguration { - fn default() -> Self { - UiConfiguration { - enabled: false, - port: 8180, - interface: "127.0.0.1".into(), - hosts: Some(vec![]), - } - } -} - #[derive(Debug, PartialEq)] pub struct IpcConfiguration { pub enabled: bool, @@ -146,11 +96,11 @@ pub struct WsConfiguration { pub interface: String, pub port: u16, pub apis: ApiSet, + pub max_connections: usize, pub origins: Option>, pub hosts: Option>, pub signer_path: PathBuf, pub support_token_api: bool, - pub ui_address: Option, pub dapps_address: Option, } @@ -162,11 +112,11 @@ impl Default for WsConfiguration { interface: "127.0.0.1".into(), port: 8546, apis: ApiSet::UnsafeContext, + max_connections: 100, origins: Some(vec!["parity://*".into(),"chrome-extension://*".into(), "moz-extension://*".into()]), hosts: Some(Vec::new()), signer_path: replace_home(&data_dir, "$BASE/signer").into(), support_token_api: true, - ui_address: None, dapps_address: Some("127.0.0.1:8545".into()), } } @@ -208,7 +158,6 @@ pub fn new_ws( let url = format!("{}:{}", conf.interface, conf.port); let addr = url.parse().map_err(|_| format!("Invalid WebSockets listen host/port given: {}", url))?; - let full_handler = setup_apis(rpc_apis::ApiSet::SafeContext, deps); let handler = { let mut handler = MetaIoHandler::with_middleware(( @@ -222,9 +171,8 @@ pub fn new_ws( }; let remote = deps.remote.clone(); - let ui_address = conf.ui_address.clone(); - let allowed_origins = into_domains(with_domain(conf.origins, domain, &ui_address, &conf.dapps_address)); - let allowed_hosts = into_domains(with_domain(conf.hosts, domain, &Some(url.clone().into()), &None)); + let allowed_origins = into_domains(with_domain(conf.origins, domain, &conf.dapps_address)); + let allowed_hosts = into_domains(with_domain(conf.hosts, domain, &Some(url.clone().into()))); let signer_path; let path = match conf.support_token_api { @@ -240,6 +188,7 @@ pub fn new_ws( remote.clone(), allowed_origins, allowed_hosts, + conf.max_connections, rpc::WsExtractor::new(path.clone()), rpc::WsExtractor::new(path.clone()), rpc::WsStats::new(deps.stats.clone()), @@ -272,7 +221,7 @@ pub fn new_http( let remote = deps.remote.clone(); let cors_domains = into_domains(conf.cors); - let allowed_hosts = into_domains(with_domain(conf.hosts, domain, &Some(url.clone().into()), &None)); + let allowed_hosts = into_domains(with_domain(conf.hosts, domain, &Some(url.clone().into()))); let start_result = rpc::start_http( &addr, @@ -324,7 +273,7 @@ fn into_domains>(items: Option>) -> DomainsValidatio items.map(|vals| vals.into_iter().map(T::from).collect()).into() } -fn with_domain(items: Option>, domain: &str, ui_address: &Option, dapps_address: &Option) -> Option> { +fn with_domain(items: Option>, domain: &str, dapps_address: &Option) -> Option> { fn extract_port(s: &str) -> Option { s.split(':').nth(1).and_then(|s| s.parse().ok()) } @@ -343,14 +292,13 @@ fn with_domain(items: Option>, domain: &str, ui_address: &Option(apis: ApiSet, deps: &Dependencies) -> MetaIoHandler> +pub fn setup_apis(apis: ApiSet, deps: &Dependencies) -> MetaIoHandler> where D: rpc_apis::Dependencies { let mut handler = MetaIoHandler::with_middleware( diff --git a/parity/rpc_apis.rs b/parity/rpc_apis.rs index 5ba18dc09bf16b9d3c8b560661b7160ba3fd9336..d76438b15945900d0c20fd2d6bf804507473b74d 100644 --- a/parity/rpc_apis.rs +++ b/parity/rpc_apis.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -22,12 +22,13 @@ use std::sync::{Arc, Weak}; pub use parity_rpc::signer::SignerService; pub use parity_rpc::dapps::{DappsService, LocalDapp}; +use ethcore_service::PrivateTxService; use ethcore::account_provider::AccountProvider; use ethcore::client::Client; use ethcore::miner::Miner; use ethcore::snapshot::SnapshotService; use ethcore_logger::RotatingLogger; -use ethsync::{ManageNetwork, SyncProvider, LightSync}; +use sync::{ManageNetwork, SyncProvider, LightSync}; use futures_cpupool::CpuPool; use hash_fetch::fetch::Client as FetchClient; use jsonrpc_core::{self as core, MetaIoHandler}; @@ -40,6 +41,7 @@ use parity_rpc::dispatch::{FullDispatcher, LightDispatcher}; use parity_rpc::informant::{ActivityNotifier, ClientNotifier}; use parity_rpc::{Metadata, NetworkSettings, Host}; use parking_lot::{Mutex, RwLock}; +use ethcore_private_tx::Provider as PrivateTransactionManager; use updater::Updater; #[derive(Debug, PartialEq, Clone, Eq, Hash)] @@ -70,6 +72,8 @@ pub enum Api { Rpc, /// SecretStore (UNSAFE: arbitrary hash signing) SecretStore, + /// Private transaction manager (Safe) + Private, /// Whisper (Safe) // TODO: _if_ someone guesses someone else's key or filter IDs they can remove // BUT these are all ephemeral so it seems fine. @@ -98,6 +102,7 @@ impl FromStr for Api { "traces" => Ok(Traces), "rpc" => Ok(Rpc), "secretstore" => Ok(SecretStore), + "private" => Ok(Private), "shh" => Ok(Whisper), "shh_pubsub" => Ok(WhisperPubSub), api => Err(format!("Unknown api: {}", api)) @@ -111,8 +116,6 @@ pub enum ApiSet { SafeContext, // Unsafe context (like jsonrpc over http) UnsafeContext, - // Public context (like public jsonrpc over http) - PublicContext, // All possible APIs All, // Local "unsafe" context and accounts access @@ -183,6 +186,7 @@ fn to_modules(apis: &HashSet) -> BTreeMap { Api::Traces => ("traces", "1.0"), Api::Rpc => ("rpc", "1.0"), Api::SecretStore => ("secretstore", "1.0"), + Api::Private => ("private", "1.0"), Api::Whisper => ("shh", "1.0"), Api::WhisperPubSub => ("shh_pubsub", "1.0"), }; @@ -213,7 +217,8 @@ pub struct FullDependencies { pub snapshot: Arc, pub sync: Arc, pub net: Arc, - pub secret_store: Option>, + pub secret_store: Arc, + pub private_tx_service: Option>, pub miner: Arc, pub external_miner: Arc, pub logger: Arc, @@ -230,6 +235,7 @@ pub struct FullDependencies { pub remote: parity_reactor::Remote, pub whisper_rpc: Option<::whisper::RpcFactory>, pub gas_price_percentile: usize, + pub poll_lifetime: u32, } impl FullDependencies { @@ -283,12 +289,13 @@ impl FullDependencies { allow_pending_receipt_query: !self.geth_compatibility, send_block_number_in_get_work: !self.geth_compatibility, gas_price_percentile: self.gas_price_percentile, + poll_lifetime: self.poll_lifetime } ); handler.extend_with(client.to_delegate()); if !for_generic_pubsub { - let filter_client = EthFilterClient::new(self.client.clone(), self.miner.clone()); + let filter_client = EthFilterClient::new(self.client.clone(), self.miner.clone(), self.poll_lifetime); handler.extend_with(filter_client.to_delegate()); add_signing_methods!(EthSigning, handler, self, nonces.clone()); @@ -299,7 +306,7 @@ impl FullDependencies { let client = EthPubSubClient::new(self.client.clone(), self.remote.clone()); let h = client.handler(); self.miner.add_transactions_listener(Box::new(move |hashes| if let Some(h) = h.upgrade() { - h.new_transactions(hashes); + h.notify_new_transactions(hashes); })); if let Some(h) = client.handler().upgrade() { @@ -309,7 +316,7 @@ impl FullDependencies { } }, Api::Personal => { - handler.extend_with(PersonalClient::new(self.secret_store.clone(), dispatcher.clone(), self.geth_compatibility).to_delegate()); + handler.extend_with(PersonalClient::new(&self.secret_store, dispatcher.clone(), self.geth_compatibility).to_delegate()); }, Api::Signer => { handler.extend_with(SignerClient::new(&self.secret_store, dispatcher.clone(), &self.signer_service, self.remote.clone()).to_delegate()); @@ -385,7 +392,10 @@ impl FullDependencies { ); } } - } + }, + Api::Private => { + handler.extend_with(PrivateClient::new(self.private_tx_service.as_ref().map(|p| p.provider())).to_delegate()); + }, } } } @@ -437,7 +447,9 @@ pub struct LightDependencies { pub geth_compatibility: bool, pub remote: parity_reactor::Remote, pub whisper_rpc: Option<::whisper::RpcFactory>, + pub private_tx_service: Option>, pub gas_price_percentile: usize, + pub poll_lifetime: u32, } impl LightDependencies { @@ -464,7 +476,7 @@ impl LightDependencies { { let deps = &$deps; let dispatcher = dispatcher.clone(); - let secret_store = Some(deps.secret_store.clone()); + let secret_store = deps.secret_store.clone(); if deps.signer_service.is_enabled() { $handler.extend_with($namespace::to_delegate( SigningQueueClient::new(&deps.signer_service, dispatcher, deps.remote.clone(), &secret_store) @@ -495,6 +507,7 @@ impl LightDependencies { self.secret_store.clone(), self.cache.clone(), self.gas_price_percentile, + self.poll_lifetime, ); handler.extend_with(Eth::to_delegate(client.clone())); @@ -516,18 +529,16 @@ impl LightDependencies { let h = client.handler(); self.transaction_queue.write().add_listener(Box::new(move |transactions| { if let Some(h) = h.upgrade() { - h.new_transactions(transactions); + h.notify_new_transactions(transactions); } })); handler.extend_with(EthPubSub::to_delegate(client)); }, Api::Personal => { - let secret_store = Some(self.secret_store.clone()); - handler.extend_with(PersonalClient::new(secret_store, dispatcher.clone(), self.geth_compatibility).to_delegate()); + handler.extend_with(PersonalClient::new(&self.secret_store, dispatcher.clone(), self.geth_compatibility).to_delegate()); }, Api::Signer => { - let secret_store = Some(self.secret_store.clone()); - handler.extend_with(SignerClient::new(&secret_store, dispatcher.clone(), &self.signer_service, self.remote.clone()).to_delegate()); + handler.extend_with(SignerClient::new(&self.secret_store, dispatcher.clone(), &self.signer_service, self.remote.clone()).to_delegate()); }, Api::Parity => { let signer = match self.signer_service.is_enabled() { @@ -560,8 +571,7 @@ impl LightDependencies { } }, Api::ParityAccounts => { - let secret_store = Some(self.secret_store.clone()); - handler.extend_with(ParityAccountsClient::new(&secret_store).to_delegate()); + handler.extend_with(ParityAccountsClient::new(&self.secret_store).to_delegate()); }, Api::ParitySet => { handler.extend_with(light::ParitySetClient::new( @@ -579,20 +589,25 @@ impl LightDependencies { handler.extend_with(RpcClient::new(modules).to_delegate()); }, Api::SecretStore => { - let secret_store = Some(self.secret_store.clone()); - handler.extend_with(SecretStoreClient::new(&secret_store).to_delegate()); + handler.extend_with(SecretStoreClient::new(&self.secret_store).to_delegate()); }, Api::Whisper => { if let Some(ref whisper_rpc) = self.whisper_rpc { let whisper = whisper_rpc.make_handler(self.net.clone()); handler.extend_with(::parity_whisper::rpc::Whisper::to_delegate(whisper)); } - } + }, Api::WhisperPubSub => { if let Some(ref whisper_rpc) = self.whisper_rpc { let whisper = whisper_rpc.make_handler(self.net.clone()); handler.extend_with(::parity_whisper::rpc::WhisperPubSub::to_delegate(whisper)); } + }, + Api::Private => { + if let Some(ref tx_manager) = self.private_tx_service { + let private_tx_service = Some(tx_manager.clone()); + handler.extend_with(PrivateClient::new(private_tx_service).to_delegate()); + } } } } @@ -620,7 +635,7 @@ impl ApiSet { } pub fn list_apis(&self) -> HashSet { - let mut public_list = [ + let mut public_list: HashSet = [ Api::Web3, Api::Net, Api::Eth, @@ -629,11 +644,11 @@ impl ApiSet { Api::Rpc, Api::Whisper, Api::WhisperPubSub, + Api::Private, ].into_iter().cloned().collect(); match *self { ApiSet::List(ref apis) => apis.clone(), - ApiSet::PublicContext => public_list, ApiSet::UnsafeContext => { public_list.insert(Api::Traces); public_list.insert(Api::ParityPubSub); @@ -693,6 +708,7 @@ mod test { assert_eq!(Api::Traces, "traces".parse().unwrap()); assert_eq!(Api::Rpc, "rpc".parse().unwrap()); assert_eq!(Api::SecretStore, "secretstore".parse().unwrap()); + assert_eq!(Api::Private, "private".parse().unwrap()); assert_eq!(Api::Whisper, "shh".parse().unwrap()); assert_eq!(Api::WhisperPubSub, "shh_pubsub".parse().unwrap()); assert!("rp".parse::().is_err()); @@ -712,7 +728,7 @@ mod test { fn test_api_set_unsafe_context() { let expected = vec![ // make sure this list contains only SAFE methods - Api::Web3, Api::Net, Api::Eth, Api::EthPubSub, Api::Parity, Api::ParityPubSub, Api::Traces, Api::Rpc, Api::Whisper, Api::WhisperPubSub, + Api::Web3, Api::Net, Api::Eth, Api::EthPubSub, Api::Parity, Api::ParityPubSub, Api::Traces, Api::Rpc, Api::Whisper, Api::WhisperPubSub, Api::Private, ].into_iter().collect(); assert_eq!(ApiSet::UnsafeContext.list_apis(), expected); } @@ -721,7 +737,7 @@ mod test { fn test_api_set_ipc_context() { let expected = vec![ // safe - Api::Web3, Api::Net, Api::Eth, Api::EthPubSub, Api::Parity, Api::ParityPubSub, Api::Traces, Api::Rpc, Api::Whisper, Api::WhisperPubSub, + Api::Web3, Api::Net, Api::Eth, Api::EthPubSub, Api::Parity, Api::ParityPubSub, Api::Traces, Api::Rpc, Api::Whisper, Api::WhisperPubSub, Api::Private, // semi-safe Api::ParityAccounts ].into_iter().collect(); @@ -732,7 +748,7 @@ mod test { fn test_api_set_safe_context() { let expected = vec![ // safe - Api::Web3, Api::Net, Api::Eth, Api::EthPubSub, Api::Parity, Api::ParityPubSub, Api::Traces, Api::Rpc, Api::SecretStore, Api::Whisper, Api::WhisperPubSub, + Api::Web3, Api::Net, Api::Eth, Api::EthPubSub, Api::Parity, Api::ParityPubSub, Api::Traces, Api::Rpc, Api::SecretStore, Api::Whisper, Api::WhisperPubSub, Api::Private, // semi-safe Api::ParityAccounts, // Unsafe @@ -747,7 +763,8 @@ mod test { Api::Web3, Api::Net, Api::Eth, Api::EthPubSub, Api::Parity, Api::ParityPubSub, Api::Traces, Api::Rpc, Api::SecretStore, Api::Whisper, Api::WhisperPubSub, Api::ParityAccounts, Api::ParitySet, Api::Signer, - Api::Personal + Api::Personal, + Api::Private, ].into_iter().collect())); } @@ -757,13 +774,14 @@ mod test { Api::Web3, Api::Net, Api::Eth, Api::EthPubSub, Api::Parity, Api::ParityPubSub, Api::Traces, Api::Rpc, Api::SecretStore, Api::Whisper, Api::WhisperPubSub, Api::ParityAccounts, Api::ParitySet, Api::Signer, + Api::Private ].into_iter().collect())); } #[test] fn test_safe_parsing() { assert_eq!("safe".parse::().unwrap(), ApiSet::List(vec![ - Api::Web3, Api::Net, Api::Eth, Api::EthPubSub, Api::Parity, Api::ParityPubSub, Api::Traces, Api::Rpc, Api::Whisper, Api::WhisperPubSub, + Api::Web3, Api::Net, Api::Eth, Api::EthPubSub, Api::Parity, Api::ParityPubSub, Api::Traces, Api::Rpc, Api::Whisper, Api::WhisperPubSub, Api::Private, ].into_iter().collect())); } } diff --git a/parity/run.rs b/parity/run.rs index d485694b9cf1ed9252eaca3b26d25690616ddace..e6c10e9c7e7549226aa8fe2f0c7c36d56285b17c 100644 --- a/parity/run.rs +++ b/parity/run.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -14,42 +14,37 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +use std::any::Any; use std::fmt; use std::sync::{Arc, Weak}; use std::time::{Duration, Instant}; use std::thread; -use std::net::{TcpListener}; -use ansi_term::{Colour, Style}; -use ctrlc::CtrlC; +use ansi_term::Colour; use ethcore::account_provider::{AccountProvider, AccountProviderSettings}; -use ethcore::client::{Client, Mode, DatabaseCompactionProfile, VMType, BlockChainClient}; -use ethcore::db::NUM_COLUMNS; +use ethcore::client::{Client, Mode, DatabaseCompactionProfile, VMType, BlockChainClient, BlockInfo}; use ethcore::ethstore::ethkey; -use ethcore::miner::{Miner, MinerService, MinerOptions}; -use ethcore::miner::{StratumOptions, Stratum}; +use ethcore::miner::{stratum, Miner, MinerService, MinerOptions}; use ethcore::snapshot; use ethcore::spec::{SpecParams, OptimizeFor}; use ethcore::verification::queue::VerifierSettings; use ethcore_logger::{Config as LogConfig, RotatingLogger}; use ethcore_service::ClientService; -use ethsync::{self, SyncConfig}; -use fdlimit::raise_fd_limit; +use sync::{self, SyncConfig}; +use miner::work_notify::WorkPoster; use futures_cpupool::CpuPool; use hash_fetch::{self, fetch}; use informant::{Informant, LightNodeInformantData, FullNodeInformantData}; use journaldb::Algorithm; -use kvdb_rocksdb::{Database, DatabaseConfig}; use light::Cache as LightDataCache; use miner::external::ExternalMiner; use node_filter::NodeFilter; use node_health; use parity_reactor::EventLoop; -use parity_rpc::{NetworkSettings, informant, is_major_importing}; -use parking_lot::{Condvar, Mutex}; +use parity_rpc::{Origin, Metadata, NetworkSettings, informant, is_major_importing}; use updater::{UpdatePolicy, Updater}; use parity_version::version; - +use ethcore_private_tx::{ProviderConfig, EncryptorConfig, SecretStoreEncryptor}; use params::{ SpecType, Pruning, AccountsConfig, GasPricerConfig, MinerExtras, Switch, tracing_switch_to_bool, fatdb_switch_to_bool, mode_switch_to_bool @@ -61,12 +56,13 @@ use cache::CacheConfig; use user_defaults::UserDefaults; use dapps; use ipfs; +use jsonrpc_core; use modules; use rpc; use rpc_apis; use secretstore; use signer; -use url; +use db; // how often to take periodic snapshots. const SNAPSHOT_PERIOD: u64 = 5000; @@ -94,14 +90,15 @@ pub struct RunCmd { pub logger_config: LogConfig, pub miner_options: MinerOptions, pub gas_price_percentile: usize, + pub poll_lifetime: u32, pub ntp_servers: Vec, pub ws_conf: rpc::WsConfiguration, pub http_conf: rpc::HttpConfiguration, pub ipc_conf: rpc::IpcConfiguration, - pub net_conf: ethsync::NetworkConfiguration, + pub net_conf: sync::NetworkConfiguration, pub network_id: Option, pub warp_sync: bool, - pub public_node: bool, + pub warp_barrier: Option, pub acc_conf: AccountsConfig, pub gas_pricer_conf: GasPricerConfig, pub miner_extras: MinerExtras, @@ -116,13 +113,14 @@ pub struct RunCmd { pub net_settings: NetworkSettings, pub dapps_conf: dapps::Configuration, pub ipfs_conf: ipfs::Configuration, - pub ui_conf: rpc::UiConfiguration, pub secretstore_conf: secretstore::Configuration, + pub private_provider_conf: ProviderConfig, + pub private_encryptor_conf: EncryptorConfig, + pub private_tx_enabled: bool, pub dapp: Option, - pub ui: bool, pub name: String, pub custom_bootnodes: bool, - pub stratum: Option, + pub stratum: Option, pub no_periodic_snapshot: bool, pub check_seal: bool, pub download_old_blocks: bool, @@ -134,29 +132,6 @@ pub struct RunCmd { pub no_hardcoded_sync: bool, } -pub fn open_ui(ws_conf: &rpc::WsConfiguration, ui_conf: &rpc::UiConfiguration, logger_config: &LogConfig) -> Result<(), String> { - if !ui_conf.enabled { - return Err("Cannot use UI command with UI turned off.".into()) - } - - let token = signer::generate_token_and_url(ws_conf, ui_conf, logger_config)?; - // Open a browser - url::open(&token.url); - // Print a message - println!("{}", token.message); - Ok(()) -} - -pub fn open_dapp(dapps_conf: &dapps::Configuration, rpc_conf: &rpc::HttpConfiguration, dapp: &str) -> Result<(), String> { - if !dapps_conf.enabled { - return Err("Cannot use DAPP command with Dapps turned off.".into()) - } - - let url = format!("http://{}:{}/{}/", rpc_conf.interface, rpc_conf.port, dapp); - url::open(&url); - Ok(()) -} - // node info fetcher for the local store. struct FullNodeInfo { miner: Option>, // TODO: only TXQ needed, just use that after decoupling. @@ -169,11 +144,12 @@ impl ::local_store::NodeInfo for FullNodeInfo { None => return Vec::new(), }; - let local_txs = miner.local_transactions(); - miner.pending_transactions() - .into_iter() - .chain(miner.future_transactions()) - .filter(|tx| local_txs.contains_key(&tx.hash())) + miner.local_transactions() + .values() + .filter_map(|status| match *status { + ::miner::pool::local_transactions::Status::Pending(ref tx) => Some(tx.pending().clone()), + _ => None, + }) .collect() } } @@ -181,9 +157,9 @@ impl ::local_store::NodeInfo for FullNodeInfo { type LightClient = ::light::client::Client<::light_helpers::EpochFetch>; // helper for light execution. -fn execute_light_impl(cmd: RunCmd, can_restart: bool, logger: Arc) -> Result<((bool, Option), Weak), String> { +fn execute_light_impl(cmd: RunCmd, logger: Arc) -> Result { use light::client as light_client; - use ethsync::{LightSyncParams, LightSync, ManageNetwork}; + use sync::{LightSyncParams, LightSync, ManageNetwork}; use parking_lot::{Mutex, RwLock}; // load spec @@ -204,13 +180,11 @@ fn execute_light_impl(cmd: RunCmd, can_restart: bool, logger: Arc bool { self.0.is_major_importing() } fn peers(&self) -> (usize, usize) { - let peers = ethsync::LightSyncProvider::peer_numbers(&*self.0); + let peers = sync::LightSyncProvider::peer_numbers(&*self.0); (peers.connected, peers.max) } } @@ -357,12 +322,10 @@ fn execute_light_impl(cmd: RunCmd, can_restart: bool, logger: Arc) -> Result<((bool, Option), Weak), String> { +fn execute_impl(cmd: RunCmd, logger: Arc, on_client_rq: Cr, + on_updater_rq: Rr) -> Result + where Cr: Fn(String) + 'static + Send, + Rr: Fn() + 'static + Send +{ // load spec let spec = cmd.spec.spec(&cmd.dirs.cache)?; @@ -468,10 +437,10 @@ pub fn execute_impl(cmd: RunCmd, can_restart: bool, logger: Arc) let snapshot_path = db_dirs.snapshot_path(); // execute upgrades - execute_upgrades(&cmd.dirs.base, &db_dirs, algorithm, cmd.compaction.compaction_profile(db_dirs.db_root_path().as_path()))?; + execute_upgrades(&cmd.dirs.base, &db_dirs, algorithm, &cmd.compaction)?; // create dirs used by parity - cmd.dirs.create_dirs(cmd.dapps_conf.enabled, cmd.ui_conf.enabled, cmd.secretstore_conf.enabled)?; + cmd.dirs.create_dirs(cmd.dapps_conf.enabled, cmd.acc_conf.unlocked_accounts.len() == 0, cmd.secretstore_conf.enabled)?; // run in daemon mode if let Some(pid_file) = cmd.daemon { @@ -513,7 +482,7 @@ pub fn execute_impl(cmd: RunCmd, can_restart: bool, logger: Arc) } sync_config.fork_block = spec.fork_block(); - let mut warp_sync = cmd.warp_sync; + let mut warp_sync = spec.engine.supports_warp() && cmd.warp_sync; if warp_sync { // Logging is not initialized yet, so we print directly to stderr if fat_db { @@ -527,7 +496,11 @@ pub fn execute_impl(cmd: RunCmd, can_restart: bool, logger: Arc) warp_sync = false; } } - sync_config.warp_sync = spec.engine.supports_warp() && warp_sync; + sync_config.warp_sync = match (warp_sync, cmd.warp_barrier) { + (true, Some(block)) => sync::WarpSync::OnlyAndAfter(block), + (true, _) => sync::WarpSync::Enabled, + _ => sync::WarpSync::Disabled, + }; sync_config.download_old_blocks = cmd.download_old_blocks; sync_config.serve_light = cmd.serve_light; @@ -538,23 +511,31 @@ pub fn execute_impl(cmd: RunCmd, can_restart: bool, logger: Arc) let cpu_pool = CpuPool::new(4); + // spin up event loop + let event_loop = EventLoop::spawn(); + // fetch service let fetch = fetch::Client::new().map_err(|e| format!("Error starting fetch client: {:?}", e))?; // create miner - let initial_min_gas_price = cmd.gas_pricer_conf.initial_min(); - let miner = Miner::new(cmd.miner_options, cmd.gas_pricer_conf.to_gas_pricer(fetch.clone(), cpu_pool.clone()), &spec, Some(account_provider.clone())); - miner.set_author(cmd.miner_extras.author); - miner.set_gas_floor_target(cmd.miner_extras.gas_floor_target); - miner.set_gas_ceil_target(cmd.miner_extras.gas_ceil_target); + let miner = Arc::new(Miner::new( + cmd.miner_options, + cmd.gas_pricer_conf.to_gas_pricer(fetch.clone(), cpu_pool.clone()), + &spec, + Some(account_provider.clone()) + )); + miner.set_author(cmd.miner_extras.author, None).expect("Fails only if password is Some; password is None; qed"); + miner.set_gas_range_target(cmd.miner_extras.gas_range_target); miner.set_extra_data(cmd.miner_extras.extra_data); - miner.set_minimal_gas_price(initial_min_gas_price); - miner.recalibrate_minimal_gas_price(); + if !cmd.miner_extras.work_notify.is_empty() { + miner.add_work_listener(Box::new( + WorkPoster::new(&cmd.miner_extras.work_notify, fetch.clone(), event_loop.remote()) + )); + } let engine_signer = cmd.miner_extras.engine_signer; - if engine_signer != Default::default() { // Check if engine signer exists - if !account_provider.has_account(engine_signer).unwrap_or(false) { + if !account_provider.has_account(engine_signer) { return Err(format!("Consensus signer account not found for the current chain. {}", build_create_account_hint(&cmd.spec, &cmd.dirs.keys))); } @@ -564,13 +545,13 @@ pub fn execute_impl(cmd: RunCmd, can_restart: bool, logger: Arc) } // Attempt to sign in the engine signer. - if !passwords.iter().any(|p| miner.set_engine_signer(engine_signer, (*p).clone()).is_ok()) { + if !passwords.iter().any(|p| miner.set_author(engine_signer, Some(p.to_owned())).is_ok()) { return Err(format!("No valid password for the consensus signer {}. {}", engine_signer, VERIFY_PASSWORD_HINT)); } } // display warning if using --no-hardcoded-sync - if !cmd.no_hardcoded_sync { + if cmd.no_hardcoded_sync { warn!("The --no-hardcoded-sync flag has no effect if you don't use --light"); } @@ -602,14 +583,21 @@ pub fn execute_impl(cmd: RunCmd, can_restart: bool, logger: Arc) // set network path. net_conf.net_config_path = Some(db_dirs.network_path().to_string_lossy().into_owned()); + let client_db = db::open_client_db(&client_path, &client_config)?; + let restoration_db_handler = db::restoration_db_handler(&client_path, &client_config); + // create client service. let service = ClientService::start( client_config, &spec, - &client_path, + client_db, &snapshot_path, + restoration_db_handler, &cmd.dirs.ipc_path(), miner.clone(), + account_provider.clone(), + Box::new(SecretStoreEncryptor::new(cmd.private_encryptor_conf, fetch.clone()).map_err(|e| e.to_string())?), + cmd.private_provider_conf, ).map_err(|e| format!("Client service error: {:?}", e))?; let connection_filter_address = spec.params().node_permission_contract; @@ -618,6 +606,12 @@ pub fn execute_impl(cmd: RunCmd, can_restart: bool, logger: Arc) // take handle to client let client = service.client(); + // Update miners block gas limit + miner.update_transaction_queue_limits(*client.best_block_header().gas_limit()); + + // take handle to private transactions service + let private_tx_service = service.private_tx_service(); + let private_tx_provider = private_tx_service.provider(); let connection_filter = connection_filter_address.map(|a| Arc::new(NodeFilter::new(Arc::downgrade(&client) as Weak, a))); let snapshot_service = service.snapshot_service(); @@ -664,7 +658,7 @@ pub fn execute_impl(cmd: RunCmd, can_restart: bool, logger: Arc) // start stratum if let Some(ref stratum_config) = cmd.stratum { - Stratum::register(stratum_config, miner.clone(), Arc::downgrade(&client)) + stratum::Stratum::register(stratum_config, miner.clone(), Arc::downgrade(&client)) .map_err(|e| format!("Stratum start error: {:?}", e))?; } @@ -685,15 +679,22 @@ pub fn execute_impl(cmd: RunCmd, can_restart: bool, logger: Arc) net_conf.clone().into(), client.clone(), snapshot_service.clone(), + private_tx_service.clone(), client.clone(), &cmd.logger_config, attached_protos, - connection_filter.clone().map(|f| f as Arc<::ethsync::ConnectionFilter + 'static>), + connection_filter.clone().map(|f| f as Arc<::sync::ConnectionFilter + 'static>), ).map_err(|e| format!("Sync error: {}", e))?; service.add_notify(chain_notify.clone()); - if let Some(filter) = connection_filter { - service.add_notify(filter); + + // provider not added to a notification center is effectively disabled + // TODO [debris] refactor it later on + if cmd.private_tx_enabled { + service.add_notify(private_tx_provider.clone()); + // TODO [ToDr] PrivateTX should use separate notifications + // re-using ChainNotify for this is a bit abusive. + private_tx_provider.add_notify(chain_notify.clone()); } // start network @@ -701,9 +702,6 @@ pub fn execute_impl(cmd: RunCmd, can_restart: bool, logger: Arc) chain_notify.start(); } - // spin up event loop - let event_loop = EventLoop::spawn(); - let contract_client = Arc::new(::dapps::FullRegistrar::new(client.clone())); // the updater service @@ -718,18 +716,14 @@ pub fn execute_impl(cmd: RunCmd, can_restart: bool, logger: Arc) // set up dependencies for rpc servers let rpc_stats = Arc::new(informant::RpcStats::default()); - let secret_store = match cmd.public_node { - true => None, - false => Some(account_provider.clone()) - }; - + let secret_store = account_provider.clone(); let signer_service = Arc::new(signer::new_service(&cmd.ws_conf, &cmd.logger_config)); // the dapps server let (node_health, dapps_deps) = { let (sync, client) = (sync_provider.clone(), client.clone()); - struct SyncStatus(Arc, Arc, ethsync::NetworkConfiguration); + struct SyncStatus(Arc, Arc, sync::NetworkConfiguration); impl fmt::Debug for SyncStatus { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { write!(fmt, "Dapps Sync Status") @@ -758,11 +752,9 @@ pub fn execute_impl(cmd: RunCmd, can_restart: bool, logger: Arc) fetch: fetch.clone(), pool: cpu_pool.clone(), signer: signer_service.clone(), - ui_address: cmd.ui_conf.redirection_address(), }) }; let dapps_middleware = dapps::new(cmd.dapps_conf.clone(), dapps_deps.clone())?; - let ui_middleware = dapps::new_ui(cmd.ui_conf.enabled, dapps_deps)?; let dapps_service = dapps::service(&dapps_middleware); let deps_for_rpc_apis = Arc::new(rpc_apis::FullDependencies { @@ -787,7 +779,9 @@ pub fn execute_impl(cmd: RunCmd, can_restart: bool, logger: Arc) pool: cpu_pool.clone(), remote: event_loop.remote(), whisper_rpc: whisper_factory, + private_tx_service: Some(private_tx_service.clone()), gas_price_percentile: cmd.gas_price_percentile, + poll_lifetime: cmd.poll_lifetime, }); let dependencies = rpc::Dependencies { @@ -803,16 +797,16 @@ pub fn execute_impl(cmd: RunCmd, can_restart: bool, logger: Arc) }; // start rpc servers + let rpc_direct = rpc::setup_apis(rpc_apis::ApiSet::All, &dependencies); let ws_server = rpc::new_ws(cmd.ws_conf.clone(), &dependencies)?; let ipc_server = rpc::new_ipc(cmd.ipc_conf, &dependencies)?; let http_server = rpc::new_http("HTTP JSON-RPC", "jsonrpc", cmd.http_conf.clone(), &dependencies, dapps_middleware)?; - // the ui server - let ui_server = rpc::new_http("UI WALLET", "ui", cmd.ui_conf.clone().into(), &dependencies, ui_middleware)?; // secret store key server let secretstore_deps = secretstore::Dependencies { client: client.clone(), sync: sync_provider.clone(), + miner: miner, account_provider: account_provider, accounts_passwords: &passwords, }; @@ -852,7 +846,7 @@ pub fn execute_impl(cmd: RunCmd, can_restart: bool, logger: Arc) }); // the watcher must be kept alive. - let _watcher = match cmd.no_periodic_snapshot { + let watcher = match cmd.no_periodic_snapshot { true => None, false => { let sync = sync_provider.clone(); @@ -870,62 +864,116 @@ pub fn execute_impl(cmd: RunCmd, can_restart: bool, logger: Arc) }, }; - // start ui - if cmd.ui { - open_ui(&cmd.ws_conf, &cmd.ui_conf, &cmd.logger_config)?; - } - - if let Some(dapp) = cmd.dapp { - open_dapp(&cmd.dapps_conf, &cmd.http_conf, &dapp)?; - } - - // Create a weak reference to the client so that we can wait on shutdown until it is dropped - let weak_client = Arc::downgrade(&client); - - // Handle exit - let restart = wait_for_exit(Some(updater), Some(client), can_restart); + client.set_exit_handler(on_client_rq); + updater.set_exit_handler(on_updater_rq); - info!("Finishing work, please wait..."); - - // drop this stuff as soon as exit detected. - drop((ws_server, http_server, ipc_server, ui_server, secretstore_key_server, ipfs_server, event_loop)); + Ok(RunningClient { + inner: RunningClientInner::Full { + rpc: rpc_direct, + informant, + client, + client_service: Arc::new(service), + keep_alive: Box::new((watcher, updater, ws_server, http_server, ipc_server, secretstore_key_server, ipfs_server, event_loop)), + } + }) +} - // to make sure timer does not spawn requests while shutdown is in progress - informant.shutdown(); - // just Arc is dropping here, to allow other reference release in its default time - drop(informant); +/// Parity client currently executing in background threads. +/// +/// Should be destroyed by calling `shutdown()`, otherwise execution will continue in the +/// background. +pub struct RunningClient { + inner: RunningClientInner, +} - Ok((restart, weak_client)) +enum RunningClientInner { + Light { + rpc: jsonrpc_core::MetaIoHandler>, + informant: Arc>, + client: Arc, + keep_alive: Box, + }, + Full { + rpc: jsonrpc_core::MetaIoHandler>, + informant: Arc>, + client: Arc, + client_service: Arc, + keep_alive: Box, + }, } -pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc) -> Result<(bool, Option), String> { - if cmd.ui_conf.enabled { - warn!("{}", Style::new().bold().paint("Parity browser interface is deprecated. It's going to be removed in the next version, use standalone Parity UI instead.")); - warn!("{}", Style::new().bold().paint("Standalone Parity UI: https://github.com/Parity-JS/shell/releases")); - } +impl RunningClient { + /// Performs a synchronous RPC query. + /// Blocks execution until the result is ready. + pub fn rpc_query_sync(&self, request: &str) -> Option { + let metadata = Metadata { + origin: Origin::CApi, + session: None, + }; - if cmd.ui && cmd.dapps_conf.enabled { - // Check if Parity is already running - let addr = format!("{}:{}", cmd.ui_conf.interface, cmd.ui_conf.port); - if !TcpListener::bind(&addr as &str).is_ok() { - return open_ui(&cmd.ws_conf, &cmd.ui_conf, &cmd.logger_config).map(|_| (false, None)); + match self.inner { + RunningClientInner::Light { ref rpc, .. } => { + rpc.handle_request_sync(request, metadata) + }, + RunningClientInner::Full { ref rpc, .. } => { + rpc.handle_request_sync(request, metadata) + }, } } - // increase max number of open files - raise_fd_limit(); - - fn wait(res: Result<((bool, Option), Weak), String>) -> Result<(bool, Option), String> { - res.map(|(restart, weak_client)| { - wait_for_drop(weak_client); - restart - }) + /// Shuts down the client. + pub fn shutdown(self) { + match self.inner { + RunningClientInner::Light { rpc, informant, client, keep_alive } => { + // Create a weak reference to the client so that we can wait on shutdown + // until it is dropped + let weak_client = Arc::downgrade(&client); + drop(rpc); + drop(keep_alive); + informant.shutdown(); + drop(informant); + drop(client); + wait_for_drop(weak_client); + }, + RunningClientInner::Full { rpc, informant, client, client_service, keep_alive } => { + info!("Finishing work, please wait..."); + // Create a weak reference to the client so that we can wait on shutdown + // until it is dropped + let weak_client = Arc::downgrade(&client); + // Shutdown and drop the ServiceClient + client_service.shutdown(); + drop(client_service); + // drop this stuff as soon as exit detected. + drop(rpc); + drop(keep_alive); + // to make sure timer does not spawn requests while shutdown is in progress + informant.shutdown(); + // just Arc is dropping here, to allow other reference release in its default time + drop(informant); + drop(client); + wait_for_drop(weak_client); + } + } } +} +/// Executes the given run command. +/// +/// `on_client_rq` is the action to perform when the client receives an RPC request to be restarted +/// with a different chain. +/// +/// `on_updater_rq` is the action to perform when the updater has a new binary to execute. +/// +/// On error, returns what to print on stderr. +pub fn execute(cmd: RunCmd, logger: Arc, + on_client_rq: Cr, on_updater_rq: Rr) -> Result + where Cr: Fn(String) + 'static + Send, + Rr: Fn() + 'static + Send +{ if cmd.light { - wait(execute_light_impl(cmd, can_restart, logger)) + execute_light_impl(cmd, logger) } else { - wait(execute_impl(cmd, can_restart, logger)) + execute_impl(cmd, logger, on_client_rq, on_updater_rq) } } @@ -983,7 +1031,7 @@ fn prepare_account_provider(spec: &SpecType, dirs: &Directories, data_dir: &str, for a in cfg.unlocked_accounts { // Check if the account exists - if !account_provider.has_account(a).unwrap_or(false) { + if !account_provider.has_account(a) { return Err(format!("Account {} not found for the current chain. {}", a, build_create_account_hint(spec, &dirs.keys))); } @@ -1008,7 +1056,7 @@ fn prepare_account_provider(spec: &SpecType, dirs: &Directories, data_dir: &str, fn insert_dev_account(account_provider: &AccountProvider) { let secret: ethkey::Secret = "4d5db4107d237df6a3d58ee5f70ae63d73d7658d4026f2eefd2f204c81682cb7".into(); let dev_account = ethkey::KeyPair::from_secret(secret.clone()).expect("Valid secret produces valid key;qed"); - if let Ok(false) = account_provider.has_account(dev_account.address()) { + if !account_provider.has_account(dev_account.address()) { match account_provider.insert_account(secret, "") { Err(e) => warn!("Unable to add development account: {}", e), Ok(address) => { @@ -1027,39 +1075,6 @@ fn build_create_account_hint(spec: &SpecType, keys: &str) -> String { format!("You can create an account via RPC, UI or `parity account new --chain {} --keys-path {}`.", spec, keys) } -fn wait_for_exit( - updater: Option>, - client: Option>, - can_restart: bool -) -> (bool, Option) { - let exit = Arc::new((Mutex::new((false, None)), Condvar::new())); - - // Handle possible exits - let e = exit.clone(); - CtrlC::set_handler(move || { e.1.notify_all(); }); - - if can_restart { - if let Some(updater) = updater { - // Handle updater wanting to restart us - let e = exit.clone(); - updater.set_exit_handler(move || { *e.0.lock() = (true, None); e.1.notify_all(); }); - } - - if let Some(client) = client { - // Handle updater wanting to restart us - let e = exit.clone(); - client.set_exit_handler(move |restart, new_chain: Option| { *e.0.lock() = (restart, new_chain); e.1.notify_all(); }); - } - } else { - trace!(target: "mode", "Not hypervised: not setting exit handlers."); - } - - // Wait for signal - let mut l = exit.0.lock(); - let _ = exit.1.wait(&mut l); - l.clone() -} - fn wait_for_drop(w: Weak) { let sleep_duration = Duration::from_secs(1); let warn_timeout = Duration::from_secs(60); diff --git a/parity/secretstore.rs b/parity/secretstore.rs index c71f2c14da669e99b7d6fbfa89c49f70489eb088..e018897bd783f2a934ecbeddc17687741d5dbc3c 100644 --- a/parity/secretstore.rs +++ b/parity/secretstore.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -20,8 +20,9 @@ use dir::default_data_path; use dir::helpers::replace_home; use ethcore::account_provider::AccountProvider; use ethcore::client::Client; +use ethcore::miner::Miner; use ethkey::{Secret, Public}; -use ethsync::SyncProvider; +use sync::SyncProvider; use ethereum_types::Address; /// This node secret key. @@ -49,16 +50,26 @@ pub struct Configuration { pub enabled: bool, /// Is HTTP API enabled? pub http_enabled: bool, - /// Is ACL check enabled. - pub acl_check_enabled: bool, /// Is auto migrate enabled. pub auto_migrate_enabled: bool, + /// ACL check contract address. + pub acl_check_contract_address: Option, /// Service contract address. pub service_contract_address: Option, + /// Server key generation service contract address. + pub service_contract_srv_gen_address: Option, + /// Server key retrieval service contract address. + pub service_contract_srv_retr_address: Option, + /// Document key store service contract address. + pub service_contract_doc_store_address: Option, + /// Document key shadow retrieval service contract address. + pub service_contract_doc_sretr_address: Option, /// This node secret. pub self_secret: Option, /// Other nodes IDs + addresses. pub nodes: BTreeMap, + /// Key Server Set contract address. If None, 'nodes' map is used. + pub key_server_set_contract_address: Option, /// Interface to listen to pub interface: String, /// Port to listen to @@ -79,6 +90,8 @@ pub struct Dependencies<'a> { pub client: Arc, /// Sync provider. pub sync: Arc, + /// Miner service. + pub miner: Arc, /// Account provider. pub account_provider: Arc, /// Passed accounts passwords. @@ -100,14 +113,22 @@ mod server { } } -#[cfg(feature="secretstore")] +#[cfg(feature = "secretstore")] mod server { use std::sync::Arc; use ethcore_secretstore; use ethkey::KeyPair; use ansi_term::Colour::Red; + use db; use super::{Configuration, Dependencies, NodeSecretKey, ContractAddress}; + fn into_service_contract_address(address: ContractAddress) -> ethcore_secretstore::ContractAddress { + match address { + ContractAddress::Registry => ethcore_secretstore::ContractAddress::Registry, + ContractAddress::Address(address) => ethcore_secretstore::ContractAddress::Address(address), + } + } + /// Key server pub struct KeyServer { _key_server: Box, @@ -116,7 +137,7 @@ mod server { impl KeyServer { /// Create new key server pub fn new(mut conf: Configuration, deps: Dependencies) -> Result { - if !conf.acl_check_enabled { + if conf.acl_check_contract_address.is_none() { warn!("Running SecretStore with disabled ACL check: {}", Red.bold().paint("everyone has access to stored keys")); } @@ -125,7 +146,7 @@ mod server { KeyPair::from_secret(secret).map_err(|e| format!("invalid secret: {}", e))?)), Some(NodeSecretKey::KeyStore(account)) => { // Check if account exists - if !deps.account_provider.has_account(account.clone()).unwrap_or(false) { + if !deps.account_provider.has_account(account.clone()) { return Err(format!("Account {} passed as secret store node key is not found", account)); } @@ -150,12 +171,12 @@ mod server { address: conf.http_interface.clone(), port: conf.http_port, }) } else { None }, - service_contract_address: conf.service_contract_address.map(|c| match c { - ContractAddress::Registry => ethcore_secretstore::ContractAddress::Registry, - ContractAddress::Address(address) => ethcore_secretstore::ContractAddress::Address(address), - }), - data_path: conf.data_path.clone(), - acl_check_enabled: conf.acl_check_enabled, + service_contract_address: conf.service_contract_address.map(into_service_contract_address), + service_contract_srv_gen_address: conf.service_contract_srv_gen_address.map(into_service_contract_address), + service_contract_srv_retr_address: conf.service_contract_srv_retr_address.map(into_service_contract_address), + service_contract_doc_store_address: conf.service_contract_doc_store_address.map(into_service_contract_address), + service_contract_doc_sretr_address: conf.service_contract_doc_sretr_address.map(into_service_contract_address), + acl_check_contract_address: conf.acl_check_contract_address.map(into_service_contract_address), cluster_config: ethcore_secretstore::ClusterConfiguration { threads: 4, listener_address: ethcore_secretstore::NodeAddress { @@ -166,6 +187,7 @@ mod server { address: ip, port: port, })).collect(), + key_server_set_contract_address: conf.key_server_set_contract_address.map(into_service_contract_address), allow_connecting_to_higher_nodes: true, admin_public: conf.admin_public, auto_migrate_enabled: conf.auto_migrate_enabled, @@ -174,7 +196,8 @@ mod server { cconf.cluster_config.nodes.insert(self_secret.public().clone(), cconf.cluster_config.listener_address.clone()); - let key_server = ethcore_secretstore::start(deps.client, deps.sync, self_secret, cconf) + let db = db::open_secretstore_db(&conf.data_path)?; + let key_server = ethcore_secretstore::start(deps.client, deps.sync, deps.miner, self_secret, cconf, db) .map_err(|e| format!("Error starting KeyServer {}: {}", key_server_name, e))?; Ok(KeyServer { @@ -192,12 +215,17 @@ impl Default for Configuration { Configuration { enabled: true, http_enabled: true, - acl_check_enabled: true, auto_migrate_enabled: true, + acl_check_contract_address: Some(ContractAddress::Registry), service_contract_address: None, + service_contract_srv_gen_address: None, + service_contract_srv_retr_address: None, + service_contract_doc_store_address: None, + service_contract_doc_sretr_address: None, self_secret: None, admin_public: None, nodes: BTreeMap::new(), + key_server_set_contract_address: Some(ContractAddress::Registry), interface: "127.0.0.1".to_owned(), port: 8083, http_interface: "127.0.0.1".to_owned(), diff --git a/parity/signer.rs b/parity/signer.rs index ab476ef9d7e54324d0374db48c103c7ea2deb7e2..e9a636bf8bcc75a733bb8375faacc2fc7c821b46 100644 --- a/parity/signer.rs +++ b/parity/signer.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -28,7 +28,6 @@ pub const CODES_FILENAME: &'static str = "authcodes"; pub struct NewToken { pub token: String, - pub url: String, pub message: String, } @@ -49,45 +48,26 @@ pub fn codes_path(path: &Path) -> PathBuf { p } -pub fn execute(ws_conf: rpc::WsConfiguration, ui_conf: rpc::UiConfiguration, logger_config: LogConfig) -> Result { - Ok(generate_token_and_url(&ws_conf, &ui_conf, &logger_config)?.message) +pub fn execute(ws_conf: rpc::WsConfiguration, logger_config: LogConfig) -> Result { + Ok(generate_token_and_url(&ws_conf, &logger_config)?.message) } -pub fn generate_token_and_url(ws_conf: &rpc::WsConfiguration, ui_conf: &rpc::UiConfiguration, logger_config: &LogConfig) -> Result { +pub fn generate_token_and_url(ws_conf: &rpc::WsConfiguration, logger_config: &LogConfig) -> Result { let code = generate_new_token(&ws_conf.signer_path, logger_config.color).map_err(|err| format!("Error generating token: {:?}", err))?; - let auth_url = format!("http://{}:{}/#/auth?token={}", ui_conf.interface, ui_conf.port, code); let colored = |s: String| match logger_config.color { true => format!("{}", White.bold().paint(s)), false => s, }; - if !ui_conf.enabled { - return Ok(NewToken { - token: code.clone(), - url: auth_url.clone(), - message: format!( - r#" -Generated token: -{} -"#, - colored(code) - ), - }) - } - - // And print in to the console Ok(NewToken { token: code.clone(), - url: auth_url.clone(), message: format!( r#" -Open: {} -to authorize your browser. -Or use the generated token: -{}"#, - colored(auth_url), - code - ) +Generated token: +{} +"#, + colored(code) + ), }) } diff --git a/parity/snapshot.rs b/parity/snapshot.rs index bda5059f7e507111f2011012723d961b92d40fa1..90ae8327a6179470b8e2c59f83ea8dffe09bf5a5 100644 --- a/parity/snapshot.rs +++ b/parity/snapshot.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -21,6 +21,7 @@ use std::path::{Path, PathBuf}; use std::sync::Arc; use hash::keccak; +use ethcore::account_provider::AccountProvider; use ethcore::snapshot::{Progress, RestorationStatus, SnapshotService as SS}; use ethcore::snapshot::io::{SnapshotReader, PackedReader, PackedWriter}; use ethcore::snapshot::service::Service as SnapshotService; @@ -34,7 +35,8 @@ use params::{SpecType, Pruning, Switch, tracing_switch_to_bool, fatdb_switch_to_ use helpers::{to_client_config, execute_upgrades}; use dir::Directories; use user_defaults::UserDefaults; -use fdlimit; +use ethcore_private_tx; +use db; /// Kinds of snapshot commands. #[derive(Debug, PartialEq, Clone, Copy)] @@ -120,6 +122,7 @@ fn restore_using(snapshot: Arc, reader: &R, match snapshot.status() { RestorationStatus::Ongoing { .. } => Err("Snapshot file is incomplete and missing chunks.".into()), + RestorationStatus::Initializing { .. } => Err("Snapshot restoration is still initializing.".into()), RestorationStatus::Failed => Err("Snapshot restoration failed.".into()), RestorationStatus::Inactive => { info!("Restoration complete."); @@ -146,8 +149,6 @@ impl SnapshotCommand { // load user defaults let user_defaults = UserDefaults::load(&user_defaults_path)?; - fdlimit::raise_fd_limit(); - // select pruning algorithm let algorithm = self.pruning.to_algorithm(&user_defaults); @@ -162,7 +163,7 @@ impl SnapshotCommand { let snapshot_path = db_dirs.snapshot_path(); // execute upgrades - execute_upgrades(&self.dirs.base, &db_dirs, algorithm, self.compaction.compaction_profile(db_dirs.db_root_path().as_path()))?; + execute_upgrades(&self.dirs.base, &db_dirs, algorithm, &self.compaction)?; // prepare client config let client_config = to_client_config( @@ -181,13 +182,22 @@ impl SnapshotCommand { true ); + let client_db = db::open_client_db(&client_path, &client_config)?; + let restoration_db_handler = db::restoration_db_handler(&client_path, &client_config); + let service = ClientService::start( client_config, &spec, - &client_path, + client_db, &snapshot_path, + restoration_db_handler, &self.dirs.ipc_path(), - Arc::new(Miner::with_spec(&spec)) + // TODO [ToDr] don't use test miner here + // (actually don't require miner at all) + Arc::new(Miner::new_for_tests(&spec, None)), + Arc::new(AccountProvider::transient_provider()), + Box::new(ethcore_private_tx::NoopEncryptor), + Default::default(), ).map_err(|e| format!("Client service error: {:?}", e))?; Ok(service) diff --git a/parity/stratum.rs b/parity/stratum.rs index 043ba5062239fa4dba0c48a488f670f5ae9639fc..efaa6b307ce953de18769f52202a9caf826362d6 100644 --- a/parity/stratum.rs +++ b/parity/stratum.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/parity/upgrade.rs b/parity/upgrade.rs index fcea2560eca6161c57f929f8121d48bcaa9fa4ae..d98123ce13bc4cf747c9d2e5401ab4b08f8d0ab1 100644 --- a/parity/upgrade.rs +++ b/parity/upgrade.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -16,7 +16,7 @@ //! Parity upgrade logic -use semver::Version; +use semver::{Version, SemVerError}; use std::collections::*; use std::fs::{self, File, create_dir_all}; use std::env; @@ -32,6 +32,13 @@ pub enum Error { CannotCreateConfigPath, CannotWriteVersionFile, CannotUpdateVersionFile, + SemVer(SemVerError), +} + +impl From for Error { + fn from(err: SemVerError) -> Self { + Error::SemVer(err) + } } const CURRENT_VERSION: &'static str = env!("CARGO_PKG_VERSION"); @@ -74,7 +81,7 @@ fn push_upgrades(upgrades: &mut UpgradeList) { // dummy upgrade (remove when the first one is in) upgrades.insert( - UpgradeKey { old_version: Version::parse("0.9.0").unwrap(), new_version: Version::parse("1.0.0").unwrap() }, + UpgradeKey { old_version: Version::new(0, 9, 0), new_version: Version::new(1, 0, 0)}, dummy_upgrade); } @@ -82,7 +89,7 @@ fn upgrade_from_version(previous_version: &Version) -> Result { let mut upgrades = HashMap::new(); push_upgrades(&mut upgrades); - let current_version = Version::parse(CURRENT_VERSION).unwrap(); + let current_version = Version::parse(CURRENT_VERSION)?; let mut count = 0; for upgrade_key in upgrades.keys() { @@ -114,12 +121,12 @@ fn with_locked_version(db_path: Option<&str>, script: F) -> Result HINSTANCE; +use std; +use std::os::raw::c_int; + +#[allow(unused)] +pub enum Error { + ProcessError(std::io::Error), + WindowsShellExecute(c_int), +} + +impl From for Error { + fn from(err: std::io::Error) -> Self { + Error::ProcessError(err) } +} - pub use self::winapi::SW_SHOWNORMAL as Normal; +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + match *self { + Error::ProcessError(ref e) => write!(f, "{}", e), + Error::WindowsShellExecute(e) => write!(f, "WindowsShellExecute error: {}", e), + } + } } #[cfg(windows)] -pub fn open(url: &str) { +pub fn open(url: &str) -> Result<(), Error> { use std::ffi::CString; use std::ptr; + use winapi::um::shellapi::ShellExecuteA; + use winapi::um::winuser::SW_SHOWNORMAL as Normal; + + const WINDOWS_SHELL_EXECUTE_SUCCESS: c_int = 32; - unsafe { - shell::ShellExecuteA(ptr::null_mut(), + let h_instance = unsafe { + ShellExecuteA(ptr::null_mut(), CString::new("open").unwrap().as_ptr(), CString::new(url.to_owned().replace("\n", "%0A")).unwrap().as_ptr(), ptr::null(), ptr::null(), - shell::Normal); + Normal) as c_int + }; + + // https://msdn.microsoft.com/en-us/library/windows/desktop/bb762153(v=vs.85).aspx + // `ShellExecute` returns a value greater than 32 on success + if h_instance > WINDOWS_SHELL_EXECUTE_SUCCESS { + Ok(()) + } else { + Err(Error::WindowsShellExecute(h_instance)) } } #[cfg(any(target_os="macos", target_os="freebsd"))] -pub fn open(url: &str) { - use std; - let _ = std::process::Command::new("open").arg(url).spawn(); +pub fn open(url: &str) -> Result<(), Error> { + let _ = std::process::Command::new("open").arg(url).spawn()?; + Ok(()) } #[cfg(target_os="linux")] -pub fn open(url: &str) { - use std; - let _ = std::process::Command::new("xdg-open").arg(url).spawn(); +pub fn open(url: &str) -> Result<(), Error> { + let _ = std::process::Command::new("xdg-open").arg(url).spawn()?; + Ok(()) +} + +#[cfg(target_os="android")] +pub fn open(_url: &str) -> Result<(), Error> { + // TODO: While it is generally always bad to leave a function implemented, there is not much + // more we can do here. This function will eventually be removed when we compile Parity + // as a library and not as a full binary. + Ok(()) } diff --git a/parity/user_defaults.rs b/parity/user_defaults.rs index be91e302ebe4cace355950d131116bd0097044c3..cb4a0a40a47e47a8451a5a605dfa812641b9ff78 100644 --- a/parity/user_defaults.rs +++ b/parity/user_defaults.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/parity/whisper.rs b/parity/whisper.rs index ed4812063d69cc28b0598ab8698f915412cbe2cf..c3c8854dcb6c700611d28af7994f29b5ea09615a 100644 --- a/parity/whisper.rs +++ b/parity/whisper.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -17,7 +17,7 @@ use std::sync::Arc; use std::io; -use ethsync::{AttachedProtocol, ManageNetwork}; +use sync::{AttachedProtocol, ManageNetwork}; use parity_rpc::Metadata; use parity_whisper::message::Message; use parity_whisper::net::{self as whisper_net, Network as WhisperNetwork}; @@ -89,7 +89,6 @@ pub fn setup(target_pool_size: usize, protos: &mut Vec) protos.push(AttachedProtocol { handler: net.clone() as Arc<_>, - packet_count: whisper_net::PACKET_COUNT, versions: whisper_net::SUPPORTED_VERSIONS, protocol_id: whisper_net::PROTOCOL_ID, }); @@ -97,7 +96,6 @@ pub fn setup(target_pool_size: usize, protos: &mut Vec) // parity-only extensions to whisper. protos.push(AttachedProtocol { handler: Arc::new(whisper_net::ParityExtensions), - packet_count: whisper_net::PACKET_COUNT, versions: whisper_net::SUPPORTED_VERSIONS, protocol_id: whisper_net::PARITY_PROTOCOL_ID, }); diff --git a/price-info/Cargo.toml b/price-info/Cargo.toml index 5122ebb9dd7e9160a64206b8f896c26a70574394..948f09983bae78ce6a6e49f2058ae16ddc6b483b 100644 --- a/price-info/Cargo.toml +++ b/price-info/Cargo.toml @@ -3,7 +3,7 @@ description = "Fetch current ETH price" homepage = "http://parity.io" license = "GPL-3.0" name = "price-info" -version = "1.11.0" +version = "1.12.0" authors = ["Parity Technologies "] [dependencies] @@ -16,3 +16,4 @@ serde_json = "1.0" [dev-dependencies] hyper = "0.11" parking_lot = "0.5" +fake-fetch = { path = "../util/fake-fetch" } diff --git a/price-info/src/lib.rs b/price-info/src/lib.rs index e233062b4310dd0f0d3b08efcaf0cf7a0b2f9eca..93dacca33847b7df033e62a8d66a1e2879f441fa 100644 --- a/price-info/src/lib.rs +++ b/price-info/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -25,6 +25,9 @@ extern crate serde_json; #[macro_use] extern crate log; +#[cfg(test)] +extern crate fake_fetch; + pub extern crate fetch; use std::cmp; @@ -95,8 +98,8 @@ impl Client { } /// Gets the current ETH price and calls `set_price` with the result. - pub fn get(&self, set_price: G) { - let future = self.fetch.fetch(&self.api_endpoint, fetch::Abort::default()) + pub fn get(&self, set_price: G) { + let future = self.fetch.get(&self.api_endpoint, fetch::Abort::default()) .from_err() .and_then(|response| { if !response.is_success() { @@ -133,52 +136,18 @@ impl Client { #[cfg(test)] mod test { - extern crate hyper; - extern crate parking_lot; - - use self::parking_lot::Mutex; use std::sync::Arc; - use std::sync::atomic::{AtomicBool, Ordering}; - use fetch; - use fetch::{Fetch, Url}; use futures_cpupool::CpuPool; - use futures::future::{self, FutureResult}; use Client; - use self::hyper::StatusCode; - - #[derive(Clone)] - struct FakeFetch(Option, Arc>); - - impl FakeFetch { - fn new() -> Result { - Ok(FakeFetch(None, Default::default())) - } - } - - impl Fetch for FakeFetch { - type Result = FutureResult; - - fn fetch(&self, url: &str, abort: fetch::Abort) -> Self::Result { - assert_eq!(url, "https://api.etherscan.io/api?module=stats&action=ethprice"); - let u = Url::parse(url).unwrap(); - let mut val = self.1.lock(); - *val = *val + 1; - if let Some(ref response) = self.0 { - let r = hyper::Response::new().with_body(response.clone()); - future::ok(fetch::client::Response::new(u, r, abort)) - } else { - let r = hyper::Response::new().with_status(StatusCode::NotFound); - future::ok(fetch::client::Response::new(u, r, abort)) - } - } - } + use std::sync::atomic::{AtomicBool, Ordering}; + use fake_fetch::FakeFetch; - fn price_info_ok(response: &str) -> Client { - Client::new(FakeFetch(Some(response.to_owned()), Default::default()), CpuPool::new(1)) + fn price_info_ok(response: &str) -> Client> { + Client::new(FakeFetch::new(Some(response.to_owned())), CpuPool::new(1)) } - fn price_info_not_found() -> Client { - Client::new(FakeFetch::new().unwrap(), CpuPool::new(1)) + fn price_info_not_found() -> Client> { + Client::new(FakeFetch::new(None::), CpuPool::new(1)) } #[test] diff --git a/registrar/src/lib.rs b/registrar/src/lib.rs index 961fbb17ee64adeee97ee37a7757966c5b8affc6..aad33765efef03813516c0015a30acff435303f1 100644 --- a/registrar/src/lib.rs +++ b/registrar/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/registrar/src/registrar.rs b/registrar/src/registrar.rs index c4128660d23bc78fa9a40c53974afbc21c49a6d1..0a17de499a7585769a06f969987f2bc0500964bd 100644 --- a/registrar/src/registrar.rs +++ b/registrar/src/registrar.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -74,4 +74,3 @@ pub trait RegistrarClient: Send + Sync { /// Call Contract fn call_contract(&self, address: Address, data: Bytes) -> Self::Call; } - diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml index bb23b2ec3d846bbc6e073dc21bbebd14d009b818..d227f45f535b0eb2dc26b5788dc8749bbb3f239c 100644 --- a/rpc/Cargo.toml +++ b/rpc/Cargo.toml @@ -1,7 +1,7 @@ [package] description = "Parity JSON-RPC servers." name = "parity-rpc" -version = "1.11.0" +version = "1.12.0" license = "GPL-3.0" authors = ["Parity Technologies "] @@ -17,14 +17,13 @@ multihash ="0.7" order-stat = "0.1" parking_lot = "0.5" rand = "0.4" -rust-crypto = "0.2" rustc-hex = "1.0" -semver = "0.6" +semver = "0.9" serde = "1.0" serde_derive = "1.0" serde_json = "1.0" tempdir = "0.3" -tiny-keccak = "1.3" +tiny-keccak = "1.4" tokio-timer = "0.1" transient-hashmap = "0.4" itertools = "0.5" @@ -37,21 +36,22 @@ jsonrpc-macros = { git = "https://github.com/paritytech/jsonrpc.git", branch = " jsonrpc-pubsub = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.11" } ethash = { path = "../ethash" } -ethcore = { path = "../ethcore" } +ethcore = { path = "../ethcore", features = ["test-helpers"] } ethcore-bytes = { path = "../util/bytes" } +ethcore-crypto = { path = "../ethcore/crypto" } ethcore-devtools = { path = "../devtools" } ethcore-io = { path = "../util/io" } ethcore-light = { path = "../ethcore/light" } ethcore-logger = { path = "../logger" } ethcore-miner = { path = "../miner" } +ethcore-private-tx = { path = "../ethcore/private-tx" } +ethcore-sync = { path = "../ethcore/sync" } ethcore-transaction = { path = "../ethcore/transaction" } -ethereum-types = "0.2" +ethereum-types = "0.3.2" -ethcrypto = { path = "../ethcrypto" } ethjson = { path = "../json" } ethkey = { path = "../ethkey" } ethstore = { path = "../ethstore" } -ethsync = { path = "../sync" } fetch = { path = "../util/fetch" } hardware-wallet = { path = "../hw" } keccak-hash = { path = "../util/hash" } @@ -65,7 +65,10 @@ stats = { path = "../util/stats" } vm = { path = "../ethcore/vm" } [dev-dependencies] -pretty_assertions = "0.1" -macros = { path = "../util/macros" } +ethcore = { path = "../ethcore", features = ["test-helpers"] } ethcore-network = { path = "../util/network" } +fake-fetch = { path = "../util/fake-fetch" } kvdb-memorydb = { path = "../util/kvdb-memorydb" } +macros = { path = "../util/macros" } +pretty_assertions = "0.1" +transaction-pool = { path = "../transaction-pool" } diff --git a/rpc/src/authcodes.rs b/rpc/src/authcodes.rs index d18d0741fd69b80358e7d7fcab841b8f44e4106c..5b7309a317692eafbf4efe835f54d8613ffdefc4 100644 --- a/rpc/src/authcodes.rs +++ b/rpc/src/authcodes.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/rpc/src/http_common.rs b/rpc/src/http_common.rs index 72af6e469741ab5ecc9f62da99d1ae22e8b36e7c..8296720b20f95c70988223553a2c666af0526b94 100644 --- a/rpc/src/http_common.rs +++ b/rpc/src/http_common.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/rpc/src/lib.rs b/rpc/src/lib.rs index 95a0b424b9a5c89c4b6b9ba18a55b53c3a345cc8..2d49a8c7717f2c40845e7f070e33db8e7c8f13dd 100644 --- a/rpc/src/lib.rs +++ b/rpc/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -23,7 +23,6 @@ extern crate futures; extern crate ansi_term; extern crate cid; -extern crate crypto as rust_crypto; extern crate futures_cpupool; extern crate itertools; extern crate multihash; @@ -44,19 +43,21 @@ extern crate jsonrpc_ipc_server as ipc; extern crate jsonrpc_pubsub; extern crate ethash; +#[cfg_attr(test, macro_use)] extern crate ethcore; extern crate ethcore_bytes as bytes; +extern crate ethcore_crypto as crypto; extern crate ethcore_devtools as devtools; extern crate ethcore_io as io; extern crate ethcore_light as light; +extern crate ethcore_logger; extern crate ethcore_miner as miner; +extern crate ethcore_private_tx; +extern crate ethcore_sync as sync; extern crate ethcore_transaction as transaction; -extern crate ethcrypto as crypto; extern crate ethereum_types; extern crate ethkey; extern crate ethstore; -extern crate ethsync; -extern crate ethcore_logger; extern crate vm; extern crate fetch; extern crate node_health; @@ -78,6 +79,8 @@ extern crate serde_derive; #[cfg(test)] extern crate ethjson; +#[cfg(test)] +extern crate transaction_pool as txpool; #[cfg(test)] #[macro_use] @@ -90,6 +93,9 @@ extern crate macros; #[cfg(test)] extern crate kvdb_memorydb; +#[cfg(test)] +extern crate fake_fetch; + extern crate tempdir; pub extern crate jsonrpc_ws_server as ws; @@ -175,6 +181,7 @@ pub fn start_ws( remote: tokio_core::reactor::Remote, allowed_origins: ws::DomainsValidation, allowed_hosts: ws::DomainsValidation, + max_connections: usize, extractor: T, middleware: V, stats: U, @@ -191,6 +198,7 @@ pub fn start_ws( .request_middleware(middleware) .allowed_origins(allowed_origins) .allowed_hosts(allowed_hosts) + .max_connections(max_connections) .session_stats(stats) .start(addr) } diff --git a/rpc/src/tests/helpers.rs b/rpc/src/tests/helpers.rs index db61353d538eebacad0b23ef7976849d6ec76748..602648d063c107b86e436fe76339ca7706a6e6d7 100644 --- a/rpc/src/tests/helpers.rs +++ b/rpc/src/tests/helpers.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/rpc/src/tests/mod.rs b/rpc/src/tests/mod.rs index d4d9538dcaa1e35585044fb91c125391557b3b21..6ecab3299ae51d144c6a754359e5084c11f6824e 100644 --- a/rpc/src/tests/mod.rs +++ b/rpc/src/tests/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/rpc/src/tests/rpc.rs b/rpc/src/tests/rpc.rs index 6e2900c8b7bb4ba0a0a31d06bd111a87fda94f46..015c2764a6cdc89c31143a9d55fdb838395750f3 100644 --- a/rpc/src/tests/rpc.rs +++ b/rpc/src/tests/rpc.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/rpc/src/tests/ws.rs b/rpc/src/tests/ws.rs index 6d487484588be24350e9b71016ba856d301dcd35..91f10e64758b95bf534f5a77ed82ca917bd8c6da 100644 --- a/rpc/src/tests/ws.rs +++ b/rpc/src/tests/ws.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -40,6 +40,7 @@ pub fn serve() -> (Server, usize, GuardedAuthCodes) { remote, ws::DomainsValidation::Disabled, ws::DomainsValidation::Disabled, + 5, extractors::WsExtractor::new(Some(&authcodes.path)), extractors::WsExtractor::new(Some(&authcodes.path)), extractors::WsStats::new(stats), @@ -119,7 +120,7 @@ mod testing { Host: 127.0.0.1:{}\r\n\ Connection: Close\r\n\ Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n\ - Sec-WebSocket-Protocol: {:?}_{}\r\n\ + Sec-WebSocket-Protocol: {:x}_{}\r\n\ Sec-WebSocket-Version: 13\r\n\ \r\n\ {{}} @@ -149,7 +150,7 @@ mod testing { Host: 127.0.0.1:{}\r\n\ Connection: Close\r\n\ Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n\ - Sec-WebSocket-Protocol:{:?}_{}\r\n\ + Sec-WebSocket-Protocol:{:x}_{}\r\n\ Sec-WebSocket-Version: 13\r\n\ \r\n\ {{}} @@ -176,7 +177,6 @@ mod testing { ) ); - // then assert_eq!(response1.status, "HTTP/1.1 101 Switching Protocols".to_owned()); assert_eq!(response2.status, "HTTP/1.1 403 Forbidden".to_owned()); diff --git a/rpc/src/v1/extractors.rs b/rpc/src/v1/extractors.rs index 071e57dae4345fb3f83efb349f8c4a98c860ab42..c69c41dddf32f309f74c74864bea786dbc95e969 100644 --- a/rpc/src/v1/extractors.rs +++ b/rpc/src/v1/extractors.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/helpers/accounts.rs b/rpc/src/v1/helpers/accounts.rs index 9706039355dd3b68c4b9b38a0ed7c6bfc4350e08..4268bf2f99f298154f295a1c4fdd405cb65cd3b7 100644 --- a/rpc/src/v1/helpers/accounts.rs +++ b/rpc/src/v1/helpers/accounts.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/helpers/block_import.rs b/rpc/src/v1/helpers/block_import.rs index 3c3f84635d66a759d95e9f4de626968e4cc2aad4..9e947e5baa754c32f5ffdfb6af6a45652a14f42b 100644 --- a/rpc/src/v1/helpers/block_import.rs +++ b/rpc/src/v1/helpers/block_import.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -17,7 +17,7 @@ //! Block import analysis functions. use ethcore::client::BlockQueueInfo; -use ethsync::SyncState; +use sync::SyncState; /// Check if client is during major sync or during block import. pub fn is_major_importing(sync_state: Option, queue_info: BlockQueueInfo) -> bool { @@ -32,10 +32,9 @@ pub fn is_major_importing(sync_state: Option, queue_info: BlockQueueI #[cfg(test)] mod tests { use ethcore::client::BlockQueueInfo; - use ethsync::SyncState; + use sync::SyncState; use super::is_major_importing; - fn queue_info(unverified: usize, verified: usize) -> BlockQueueInfo { BlockQueueInfo { unverified_queue_size: unverified, diff --git a/rpc/src/v1/helpers/dapps.rs b/rpc/src/v1/helpers/dapps.rs index 391a12c824d34b8fc84380f380395fced0181ed6..88a9cce6fbc901b38a7bd418fa22628b735525fd 100644 --- a/rpc/src/v1/helpers/dapps.rs +++ b/rpc/src/v1/helpers/dapps.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/helpers/dispatch.rs b/rpc/src/v1/helpers/dispatch.rs index b082320d579a8450575a883bcfe34785c81eab9c..0e6b9d443d230277c5479a61dc0ed5ac778d5021 100644 --- a/rpc/src/v1/helpers/dispatch.rs +++ b/rpc/src/v1/helpers/dispatch.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -32,10 +32,10 @@ use parking_lot::{Mutex, RwLock}; use stats::Corpus; use ethkey::Signature; -use ethsync::LightSync; +use sync::LightSync; use ethcore::ids::BlockId; -use ethcore::miner::MinerService; -use ethcore::client::MiningBlockChainClient; +use ethcore::client::BlockChainClient; +use ethcore::miner::{self, MinerService}; use ethcore::account_provider::AccountProvider; use crypto::DEFAULT_MAC; use transaction::{Action, SignedTransaction, PendingTransaction, Transaction}; @@ -117,23 +117,25 @@ impl Clone for FullDispatcher { } } -impl FullDispatcher { +impl FullDispatcher { fn state_nonce(&self, from: &Address) -> U256 { - self.miner.last_nonce(from).map(|nonce| nonce + U256::one()) - .unwrap_or_else(|| self.client.latest_nonce(from)) + self.miner.next_nonce(&*self.client, from) } /// Imports transaction to the miner's queue. - pub fn dispatch_transaction(client: &C, miner: &M, signed_transaction: PendingTransaction) -> Result { + pub fn dispatch_transaction(client: &C, miner: &M, signed_transaction: PendingTransaction, trusted: bool) -> Result { let hash = signed_transaction.transaction.hash(); - miner.import_own_transaction(client, signed_transaction) + // use `import_claimed_local_transaction` so we can decide (based on config flags) if we want to treat + // it as local or not. Nodes with public RPC interfaces will want these transactions to be treated like + // external transactions. + miner.import_claimed_local_transaction(client, signed_transaction, trusted) .map_err(errors::transaction) .map(|_| hash) } } -impl Dispatcher for FullDispatcher { +impl Dispatcher for FullDispatcher { fn fill_optional_fields(&self, request: TransactionRequest, default_sender: Address, force_nonce: bool) -> BoxFuture { @@ -181,7 +183,7 @@ impl Dispatcher for FullDispatcher Result { - Self::dispatch_transaction(&*self.client, &*self.miner, signed_transaction) + Self::dispatch_transaction(&*self.client, &*self.miner, signed_transaction, true) } } @@ -238,7 +240,7 @@ pub fn fetch_gas_price_corpus( /// Returns a eth_sign-compatible hash of data to sign. /// The data is prepended with special message to prevent -/// chosen-plaintext attacks. +/// malicious DApps from using the function to sign forged transactions. pub fn eth_data_hash(mut data: Bytes) -> H256 { let mut message_data = format!("\x19Ethereum Signed Message:\n{}", data.len()) @@ -675,9 +677,16 @@ pub fn execute( }, ConfirmationPayload::EthSignMessage(address, data) => { if accounts.is_hardware_address(&address) { - return Box::new(future::err(errors::unsupported("Signing via hardware wallets is not supported.", None))); - } + let signature = accounts.sign_message_with_hardware(&address, &data) + .map(|s| H520(s.into_electrum())) + .map(RpcH520::from) + .map(ConfirmationResponse::Signature) + // TODO: is this correct? I guess the `token` is the wallet in this context + .map(WithToken::No) + .map_err(|e| errors::account("Error signing message with hardware_wallet", e)); + return Box::new(future::done(signature)); + } let hash = eth_data_hash(data); let res = signature(&accounts, address, hash, pass) .map(|result| result @@ -721,7 +730,7 @@ fn hardware_signature(accounts: &AccountProvider, address: Address, t: Transacti let mut stream = rlp::RlpStream::new(); t.rlp_append_unsigned_transaction(&mut stream, chain_id); - let signature = accounts.sign_with_hardware(address, &t, chain_id, &stream.as_raw()) + let signature = accounts.sign_transaction_with_hardware(&address, &t, chain_id, &stream.as_raw()) .map_err(|e| { debug!(target: "miner", "Error signing transaction with hardware wallet: {}", e); errors::account("Error signing transaction with hardware wallet", e) @@ -747,7 +756,7 @@ fn decrypt(accounts: &AccountProvider, address: Address, msg: Bytes, password: S /// Extract the default gas price from a client and miner. pub fn default_gas_price(client: &C, miner: &M, percentile: usize) -> U256 where - C: MiningBlockChainClient, + C: BlockChainClient, M: MinerService, { client.gas_price_corpus(100).percentile(percentile).cloned().unwrap_or_else(|| miner.sensible_gas_price()) diff --git a/rpc/src/v1/helpers/errors.rs b/rpc/src/v1/helpers/errors.rs index 9393fc53a01247ebd3fbbfd1655c8eb6913ad74d..6207d4542fcdda727ab9a59fc0d7c0966c4da96a 100644 --- a/rpc/src/v1/helpers/errors.rs +++ b/rpc/src/v1/helpers/errors.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -19,10 +19,12 @@ use std::fmt; use ethcore::account_provider::{SignError as AccountError}; -use ethcore::error::{Error as EthcoreError, CallError}; +use ethcore::error::{Error as EthcoreError, ErrorKind, CallError}; use jsonrpc_core::{futures, Error, ErrorCode, Value}; use rlp::DecoderError; use transaction::Error as TransactionError; +use ethcore_private_tx::Error as PrivateTransactionError; +use vm::Error as VMError; mod codes { // NOTE [ToDr] Codes from [-32099, -32000] @@ -39,6 +41,7 @@ mod codes { pub const ACCOUNT_LOCKED: i64 = -32020; pub const PASSWORD_INVALID: i64 = -32021; pub const ACCOUNT_ERROR: i64 = -32023; + pub const PRIVATE_ERROR: i64 = -32024; pub const REQUEST_REJECTED: i64 = -32040; pub const REQUEST_REJECTED_LIMIT: i64 = -32041; pub const REQUEST_NOT_FOUND: i64 = -32042; @@ -65,14 +68,6 @@ pub fn light_unimplemented(details: Option) -> Error { } } -pub fn public_unsupported(details: Option) -> Error { - Error { - code: ErrorCode::ServerError(codes::UNSUPPORTED_REQUEST), - message: "Method disallowed when running parity as a public node.".into(), - data: details.map(Value::String), - } -} - pub fn unsupported>(msg: T, details: Option) -> Error { Error { code: ErrorCode::ServerError(codes::UNSUPPORTED_REQUEST), @@ -288,10 +283,26 @@ pub fn password(error: AccountError) -> Error { } } -pub fn transaction_message(error: TransactionError) -> String { +pub fn private_message(error: PrivateTransactionError) -> Error { + Error { + code: ErrorCode::ServerError(codes::PRIVATE_ERROR), + message: "Private transactions call failed.".into(), + data: Some(Value::String(format!("{:?}", error))), + } +} + +pub fn private_message_block_id_not_supported() -> Error { + Error { + code: ErrorCode::ServerError(codes::PRIVATE_ERROR), + message: "Pending block id not supported.".into(), + data: None, + } +} + +pub fn transaction_message(error: &TransactionError) -> String { use self::TransactionError::*; - match error { + match *error { AlreadyImported => "Transaction with the same hash was already imported.".into(), Old => "Transaction nonce is too low. Try incrementing the nonce.".into(), TooCheapToReplace => { @@ -312,19 +323,21 @@ pub fn transaction_message(error: TransactionError) -> String { GasLimitExceeded { limit, got } => { format!("Transaction cost exceeds current gas limit. Limit: {}, got: {}. Try decreasing supplied gas.", limit, got) }, - InvalidSignature(sig) => format!("Invalid signature: {}", sig), + InvalidSignature(ref sig) => format!("Invalid signature: {}", sig), InvalidChainId => "Invalid chain id.".into(), InvalidGasLimit(_) => "Supplied gas is beyond limit.".into(), SenderBanned => "Sender is banned in local queue.".into(), RecipientBanned => "Recipient is banned in local queue.".into(), CodeBanned => "Code is banned in local queue.".into(), NotAllowed => "Transaction is not permitted.".into(), + TooBig => "Transaction is too big, see chain specification for the limit.".into(), + InvalidRlp(ref descr) => format!("Invalid RLP data: {}", descr), } } pub fn transaction>(error: T) -> Error { let error = error.into(); - if let EthcoreError::Transaction(e) = error { + if let ErrorKind::Transaction(ref e) = *error.kind() { Error { code: ErrorCode::ServerError(codes::TRANSACTION_ERROR), message: transaction_message(e), @@ -339,6 +352,19 @@ pub fn transaction>(error: T) -> Error { } } +pub fn decode>(error: T) -> Error { + let error = error.into(); + match *error.kind() { + ErrorKind::Decoder(ref dec_err) => rlp(dec_err.clone()), + _ => Error { + code: ErrorCode::InternalError, + message: "decoding error".into(), + data: None, + } + + } +} + pub fn rlp(error: DecoderError) -> Error { Error { code: ErrorCode::InvalidParams, @@ -357,6 +383,21 @@ pub fn call(error: CallError) -> Error { } } +pub fn vm(error: &VMError, output: &[u8]) -> Error { + use rustc_hex::ToHex; + + let data = match error { + &VMError::Reverted => format!("{} 0x{}", VMError::Reverted, output.to_hex()), + error => format!("{}", error), + }; + + Error { + code: ErrorCode::ServerError(codes::EXECUTION_ERROR), + message: "VM execution error.".into(), + data: Some(Value::String(data)), + } +} + pub fn unknown_block() -> Error { Error { code: ErrorCode::InvalidParams, @@ -373,11 +414,19 @@ pub fn no_light_peers() -> Error { } } -pub fn deprecated>>(message: T) -> Error { +pub fn deprecated, T: Into>>(message: T) -> Error { Error { code: ErrorCode::ServerError(codes::DEPRECATED), message: "Method deprecated".into(), - data: message.into().map(Value::String), + data: message.into().map(Into::into).map(Value::String), + } +} + +pub fn filter_not_found() -> Error { + Error { + code: ErrorCode::ServerError(codes::UNSUPPORTED_REQUEST), + message: "Filter not found".into(), + data: None, } } diff --git a/rpc/src/v1/helpers/fake_sign.rs b/rpc/src/v1/helpers/fake_sign.rs index 84a225d8141714c2e993681976a168f2e0daa3c7..eca8a5abbd3d96410678d37f9b2afb1db0719d20 100644 --- a/rpc/src/v1/helpers/fake_sign.rs +++ b/rpc/src/v1/helpers/fake_sign.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/helpers/ipfs.rs b/rpc/src/v1/helpers/ipfs.rs index 80ad1c207b08df99517ed9b7efeaa0cb638096a4..12980d3f418c092ba5cdad7037261d7b9cf31ced 100644 --- a/rpc/src/v1/helpers/ipfs.rs +++ b/rpc/src/v1/helpers/ipfs.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -18,21 +18,15 @@ use multihash; use cid::{Cid, Codec, Version}; -use rust_crypto::sha2::Sha256; -use rust_crypto::digest::Digest; +use crypto::digest; use jsonrpc_core::Error; use v1::types::Bytes; use super::errors; /// Compute CIDv0 from protobuf encoded bytes. pub fn cid(content: Bytes) -> Result { - let mut hasher = Sha256::new(); - hasher.input(&content.0); - let len = hasher.output_bytes(); - let mut buf = Vec::with_capacity(len); - buf.resize(len, 0); - hasher.result(&mut buf); - let mh = multihash::encode(multihash::Hash::SHA2256, &buf).map_err(errors::encoding)?; + let hash = digest::sha256(&content.0); + let mh = multihash::encode(multihash::Hash::SHA2256, &*hash).map_err(errors::encoding)?; let cid = Cid::new(Codec::DagProtobuf, Version::V0, &mh); Ok(cid.to_string().into()) } diff --git a/rpc/src/v1/helpers/light_fetch.rs b/rpc/src/v1/helpers/light_fetch.rs index f0c389d5778a8f9e4614191d288138a4fd6594f5..c11f47a4566bd12c8c47fbe5bcdadcfdc0421f9a 100644 --- a/rpc/src/v1/helpers/light_fetch.rs +++ b/rpc/src/v1/helpers/light_fetch.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -25,7 +25,7 @@ use ethcore::ids::BlockId; use ethcore::filter::Filter as EthcoreFilter; use ethcore::receipt::Receipt; -use jsonrpc_core::{BoxFuture, Result}; +use jsonrpc_core::{Result, Error}; use jsonrpc_core::futures::{future, Future}; use jsonrpc_core::futures::future::Either; use jsonrpc_macros::Trailing; @@ -36,7 +36,7 @@ use light::cht; use light::on_demand::{request, OnDemand, HeaderRef, Request as OnDemandRequest, Response as OnDemandResponse}; use light::request::Field; -use ethsync::LightSync; +use sync::LightSync; use ethereum_types::{U256, Address}; use hash::H256; use parking_lot::Mutex; @@ -86,7 +86,6 @@ pub fn extract_transaction_at_index(block: encoded::Block, index: usize, eip86_t .map(|tx| Transaction::from_localized(tx, eip86_transition)) } - /// Type alias for convenience. pub type ExecutionResult = ::std::result::Result; @@ -129,9 +128,8 @@ impl LightFetch { } } BlockId::Hash(h) => { - reqs.push(request::HeaderByHash(h.into()).into()); - let idx = reqs.len(); + reqs.push(request::HeaderByHash(h.into()).into()); Ok(HeaderRef::Unresolved(idx, h.into())) } _ => Err(errors::unknown_block()) // latest, earliest, and pending will have all already returned. @@ -139,58 +137,57 @@ impl LightFetch { } /// Get a block header from the on demand service or client, or error. - pub fn header(&self, id: BlockId) -> BoxFuture { + pub fn header(&self, id: BlockId) -> impl Future + Send { let mut reqs = Vec::new(); let header_ref = match self.make_header_requests(id, &mut reqs) { Ok(r) => r, - Err(e) => return Box::new(future::err(e)), + Err(e) => return Either::A(future::err(e)), }; - - self.send_requests(reqs, |res| + Either::B(self.send_requests(reqs, |res| extract_header(&res, header_ref) .expect("these responses correspond to requests that header_ref belongs to \ therefore it will not fail; qed") - ) + )) } /// Helper for getting contract code at a given block. - pub fn code(&self, address: Address, id: BlockId) -> BoxFuture> { + pub fn code(&self, address: Address, id: BlockId) -> impl Future, Error = Error> + Send { let mut reqs = Vec::new(); let header_ref = match self.make_header_requests(id, &mut reqs) { Ok(r) => r, - Err(e) => return Box::new(future::err(e)), + Err(e) => return Either::A(future::err(e)), }; reqs.push(request::Account { header: header_ref.clone(), address: address }.into()); let account_idx = reqs.len() - 1; reqs.push(request::Code { header: header_ref, code_hash: Field::back_ref(account_idx, 0) }.into()); - self.send_requests(reqs, |mut res| match res.pop() { + Either::B(self.send_requests(reqs, |mut res| match res.pop() { Some(OnDemandResponse::Code(code)) => code, _ => panic!("responses correspond directly with requests in amount and type; qed"), - }) + })) } /// Helper for getting account info at a given block. /// `None` indicates the account doesn't exist at the given block. - pub fn account(&self, address: Address, id: BlockId) -> BoxFuture> { + pub fn account(&self, address: Address, id: BlockId) -> impl Future, Error = Error> + Send { let mut reqs = Vec::new(); let header_ref = match self.make_header_requests(id, &mut reqs) { Ok(r) => r, - Err(e) => return Box::new(future::err(e)), + Err(e) => return Either::A(future::err(e)), }; reqs.push(request::Account { header: header_ref, address: address }.into()); - self.send_requests(reqs, |mut res|match res.pop() { + Either::B(self.send_requests(reqs, |mut res|match res.pop() { Some(OnDemandResponse::Account(acc)) => acc, _ => panic!("responses correspond directly with requests in amount and type; qed"), - }) + })) } /// Helper for getting proved execution. - pub fn proved_execution(&self, req: CallRequest, num: Trailing) -> BoxFuture { + pub fn proved_execution(&self, req: CallRequest, num: Trailing) -> impl Future + Send { const DEFAULT_GAS_PRICE: u64 = 21_000; // starting gas when gas not provided. const START_GAS: u64 = 50_000; @@ -280,39 +277,39 @@ impl LightFetch { } /// Get a block itself. Fails on unknown block ID. - pub fn block(&self, id: BlockId) -> BoxFuture { + pub fn block(&self, id: BlockId) -> impl Future + Send { let mut reqs = Vec::new(); let header_ref = match self.make_header_requests(id, &mut reqs) { Ok(r) => r, - Err(e) => return Box::new(future::err(e)), + Err(e) => return Either::A(future::err(e)), }; reqs.push(request::Body(header_ref).into()); - self.send_requests(reqs, |mut res| match res.pop() { + Either::B(self.send_requests(reqs, |mut res| match res.pop() { Some(OnDemandResponse::Body(b)) => b, _ => panic!("responses correspond directly with requests in amount and type; qed"), - }) + })) } /// Get the block receipts. Fails on unknown block ID. - pub fn receipts(&self, id: BlockId) -> BoxFuture> { + pub fn receipts(&self, id: BlockId) -> impl Future, Error = Error> + Send { let mut reqs = Vec::new(); let header_ref = match self.make_header_requests(id, &mut reqs) { Ok(r) => r, - Err(e) => return Box::new(future::err(e)), + Err(e) => return Either::A(future::err(e)), }; reqs.push(request::BlockReceipts(header_ref).into()); - self.send_requests(reqs, |mut res| match res.pop() { + Either::B(self.send_requests(reqs, |mut res| match res.pop() { Some(OnDemandResponse::Receipts(b)) => b, _ => panic!("responses correspond directly with requests in amount and type; qed"), - }) + })) } /// Get transaction logs - pub fn logs(&self, filter: EthcoreFilter) -> BoxFuture> { + pub fn logs(&self, filter: EthcoreFilter) -> impl Future, Error = Error> + Send { use std::collections::BTreeMap; use jsonrpc_core::futures::stream::{self, Stream}; @@ -326,9 +323,9 @@ impl LightFetch { }; match (block_number(filter.to_block), block_number(filter.from_block)) { - (Some(to), Some(from)) if to < from => return Box::new(future::ok(Vec::new())), + (Some(to), Some(from)) if to < from => return Either::A(future::ok(Vec::new())), (Some(_), Some(_)) => {}, - _ => return Box::new(future::err(errors::unknown_block())), + _ => return Either::A(future::err(errors::unknown_block())), } let maybe_future = self.sync.with_context(move |ctx| { @@ -362,15 +359,15 @@ impl LightFetch { }); match maybe_future { - Some(fut) => Box::new(fut), - None => Box::new(future::err(errors::network_disabled())), + Some(fut) => Either::B(Either::A(fut)), + None => Either::B(Either::B(future::err(errors::network_disabled()))), } } // Get a transaction by hash. also returns the index in the block. // Only returns transactions in the canonical chain. pub fn transaction_by_hash(&self, tx_hash: H256, eip86_transition: u64) - -> BoxFuture> + -> impl Future, Error = Error> + Send { let params = (self.sync.clone(), self.on_demand.clone()); let fetcher: Self = self.clone(); @@ -426,7 +423,7 @@ impl LightFetch { })) } - fn send_requests(&self, reqs: Vec, parse_response: F) -> BoxFuture where + fn send_requests(&self, reqs: Vec, parse_response: F) -> impl Future + Send where F: FnOnce(Vec) -> T + Send + 'static, T: Send + 'static, { @@ -439,7 +436,7 @@ impl LightFetch { match maybe_future { Some(recv) => recv, - None => Box::new(future::err(errors::network_disabled())) + None => Box::new(future::err(errors::network_disabled())) as Box + Send> } } } @@ -457,7 +454,7 @@ struct ExecuteParams { // has a peer execute the transaction with given params. If `gas_known` is false, // this will double the gas on each `OutOfGas` error. -fn execute_tx(gas_known: bool, params: ExecuteParams) -> BoxFuture { +fn execute_tx(gas_known: bool, params: ExecuteParams) -> impl Future + Send { if !gas_known { Box::new(future::loop_fn(params, |mut params| { execute_tx(true, params.clone()).and_then(move |res| { @@ -480,7 +477,7 @@ fn execute_tx(gas_known: bool, params: ExecuteParams) -> BoxFuture Ok(future::Loop::Break(failed)), } }) - })) + })) as Box + Send> } else { trace!(target: "light_fetch", "Placing execution request for {} gas in on_demand", params.tx.gas); @@ -501,8 +498,8 @@ fn execute_tx(gas_known: bool, params: ExecuteParams) -> BoxFuture Box::new(fut), - None => Box::new(future::err(errors::network_disabled())), + Some(fut) => Box::new(fut) as Box + Send>, + None => Box::new(future::err(errors::network_disabled())) as Box + Send>, } } } diff --git a/rpc/src/v1/helpers/mod.rs b/rpc/src/v1/helpers/mod.rs index 7cd4e6f60a04f794fdfe2816858da08416be1782..ce2babd079963792f25d06c9a1a3433e9a07c7fa 100644 --- a/rpc/src/v1/helpers/mod.rs +++ b/rpc/src/v1/helpers/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -17,7 +17,6 @@ #[macro_use] pub mod errors; -pub mod accounts; pub mod block_import; pub mod dapps; pub mod dispatch; @@ -45,7 +44,7 @@ pub use self::requests::{ TransactionRequest, FilledTransactionRequest, ConfirmationRequest, ConfirmationPayload, CallRequest, }; pub use self::signing_queue::{ - ConfirmationsQueue, ConfirmationReceiver, ConfirmationResult, + ConfirmationsQueue, ConfirmationReceiver, ConfirmationResult, ConfirmationSender, SigningQueue, QueueEvent, DefaultAccount, QUEUE_LIMIT as SIGNING_QUEUE_LIMIT, }; diff --git a/rpc/src/v1/helpers/network_settings.rs b/rpc/src/v1/helpers/network_settings.rs index a798286244d858305cc81624216dc3fd8f51f160..d011d2394fb5edb51a31b6eed8e8b5d8cf0ae0d3 100644 --- a/rpc/src/v1/helpers/network_settings.rs +++ b/rpc/src/v1/helpers/network_settings.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -13,6 +13,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . + //! Structure to hold network settings configured from CLI /// Networking & RPC settings diff --git a/rpc/src/v1/helpers/nonce.rs b/rpc/src/v1/helpers/nonce.rs index 06f38a85898dcb00e31795c7a0d29bf84ae18515..12dfd3d52034b0725e455bfdc0d8721b23754d2f 100644 --- a/rpc/src/v1/helpers/nonce.rs +++ b/rpc/src/v1/helpers/nonce.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 harity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/helpers/oneshot.rs b/rpc/src/v1/helpers/oneshot.rs index 89e90dbd18d29e69634e9caa891c473d03fe84a9..5ede0ae912fb3bbad6bd2a45320019970920c749 100644 --- a/rpc/src/v1/helpers/oneshot.rs +++ b/rpc/src/v1/helpers/oneshot.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/helpers/poll_filter.rs b/rpc/src/v1/helpers/poll_filter.rs index 7ef8db0f15f0b6ec7fae4dabbd9f3bb88707ed13..a7e42bb406801ee5b1b45d0f9b7e123aba893957 100644 --- a/rpc/src/v1/helpers/poll_filter.rs +++ b/rpc/src/v1/helpers/poll_filter.rs @@ -1,3 +1,19 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + //! Helper type with all filter state data. use std::collections::HashSet; @@ -13,8 +29,8 @@ pub enum PollFilter { Block(BlockNumber), /// Hashes of all transactions which client was notified about. PendingTransaction(Vec), - /// Number of From block number, pending logs and log filter itself. - Logs(BlockNumber, HashSet, Filter) + /// Number of From block number, last seen block hash, pending logs and log filter itself. + Logs(BlockNumber, Option, HashSet, Filter) } /// Returns only last `n` logs diff --git a/rpc/src/v1/helpers/poll_manager.rs b/rpc/src/v1/helpers/poll_manager.rs index ab44dc35f970a7ddebbdf97506dcb636451c8f58..03387a70939f28fa223eecdb9fc98554f18756fe 100644 --- a/rpc/src/v1/helpers/poll_manager.rs +++ b/rpc/src/v1/helpers/poll_manager.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -18,9 +18,6 @@ use transient_hashmap::{TransientHashMap, Timer, StandardTimer}; -/// Lifetime of poll (in seconds). -const POLL_LIFETIME: u32 = 60; - pub type PollId = usize; /// Indexes all poll requests. @@ -32,17 +29,17 @@ pub struct PollManager where T: Timer { } impl PollManager { - /// Creates new instance of indexer. - pub fn new() -> Self { - PollManager::new_with_timer(Default::default()) + /// Creates new instance of indexer + pub fn new(lifetime: u32) -> Self { + PollManager::new_with_timer(Default::default(), lifetime) } } impl PollManager where T: Timer { - pub fn new_with_timer(timer: T) -> Self { + pub fn new_with_timer(timer: T, lifetime: u32) -> Self { PollManager { - polls: TransientHashMap::new_with_timer(POLL_LIFETIME, timer), + polls: TransientHashMap::new_with_timer(lifetime, timer), next_available_id: 0, } } @@ -74,8 +71,8 @@ impl PollManager where T: Timer { } /// Removes poll info. - pub fn remove_poll(&mut self, id: &PollId) { - self.polls.remove(id); + pub fn remove_poll(&mut self, id: &PollId) -> bool { + self.polls.remove(id).is_some() } } @@ -102,7 +99,7 @@ mod tests { time: &time, }; - let mut indexer = PollManager::new_with_timer(timer); + let mut indexer = PollManager::new_with_timer(timer,60); assert_eq!(indexer.create_poll(20), 0); assert_eq!(indexer.create_poll(20), 1); diff --git a/rpc/src/v1/helpers/requests.rs b/rpc/src/v1/helpers/requests.rs index 13bfbb1b38641bd046dc6f0f3bc5471506e4b51f..478f6785b4b16e85059d5681063ec9a78fc36d30 100644 --- a/rpc/src/v1/helpers/requests.rs +++ b/rpc/src/v1/helpers/requests.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/helpers/secretstore.rs b/rpc/src/v1/helpers/secretstore.rs index fbcc167e23924f6206d699536aba108b804f3422..f23222824fb943bec065231431b475dcebcab7ef 100644 --- a/rpc/src/v1/helpers/secretstore.rs +++ b/rpc/src/v1/helpers/secretstore.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -16,7 +16,7 @@ use std::collections::BTreeSet; use rand::{Rng, OsRng}; -use ethkey::{Public, Secret, Random, Generator, math}; +use ethkey::{self, Public, Secret, Random, Generator, math}; use crypto; use bytes::Bytes; use jsonrpc_core::Error; @@ -36,7 +36,7 @@ pub fn generate_document_key(account_public: Public, server_key_public: Public) let (common_point, encrypted_point) = encrypt_secret(document_key.public(), &server_key_public)?; // ..and now encrypt document key with account public - let encrypted_key = crypto::ecies::encrypt(&account_public, &crypto::DEFAULT_MAC, document_key.public()) + let encrypted_key = ethkey::crypto::ecies::encrypt(&account_public, &crypto::DEFAULT_MAC, document_key.public()) .map_err(errors::encryption)?; Ok(EncryptedDocumentKey { @@ -57,7 +57,7 @@ pub fn encrypt_document(key: Bytes, document: Bytes) -> Result { { let (mut encryption_buffer, iv_buffer) = encrypted_document.split_at_mut(document.len()); - crypto::aes::encrypt(&key, &iv, &document, &mut encryption_buffer); + crypto::aes::encrypt_128_ctr(&key, &iv, &document, &mut encryption_buffer).map_err(errors::encryption)?; iv_buffer.copy_from_slice(&iv); } @@ -78,7 +78,7 @@ pub fn decrypt_document(key: Bytes, mut encrypted_document: Bytes) -> Result Result<(U256, ConfirmationReceiver), QueueAddError>; - /// Removes a request from the queue. /// Notifies possible token holders that request was rejected. - fn request_rejected(&self, id: U256) -> Option; + fn request_rejected(&self, sender: ConfirmationSender) -> Option; - /// Removes a request from the queue. /// Notifies possible token holders that request was confirmed and given hash was assigned. - fn request_confirmed(&self, id: U256, result: ConfirmationResult) -> Option; + fn request_confirmed(&self, sender: ConfirmationSender, result: ConfirmationResult) -> Option; - /// Returns a request if it is contained in the queue. - fn peek(&self, id: &U256) -> Option; + /// Put a request taken from `SigningQueue::take` back to the queue. + fn request_untouched(&self, sender: ConfirmationSender); + + /// Returns and removes a request if it is contained in the queue. + fn take(&self, id: &U256) -> Option; /// Return copy of all the requests in the queue. fn requests(&self) -> Vec; @@ -96,9 +97,12 @@ pub trait SigningQueue: Send + Sync { fn is_empty(&self) -> bool; } -struct ConfirmationSender { +/// Confirmation request information with result notifier. +pub struct ConfirmationSender { + /// Confirmation request information. + pub request: ConfirmationRequest, + sender: oneshot::Sender, - request: ConfirmationRequest, } /// Receiving end of the Confirmation channel; can be used as a `Future` to await for `ConfirmationRequest` @@ -122,38 +126,31 @@ impl ConfirmationsQueue { /// Notifies consumer that the communcation is over. /// No more events will be sent after this function is invoked. pub fn finish(&self) { - self.notify(QueueEvent::Finish); + self.notify_message(QueueEvent::Finish); self.on_event.write().clear(); } + /// Notifies `ConfirmationReceiver` holder about the result given a request. + fn notify_result(&self, sender: ConfirmationSender, result: Option) -> Option { + // notify receiver about the event + self.notify_message(result.clone().map_or_else( + || QueueEvent::RequestRejected(sender.request.id), + |_| QueueEvent::RequestConfirmed(sender.request.id) + )); + + // notify confirmation receiver about resolution + let result = result.ok_or(errors::request_rejected()); + sender.sender.send(result); + + Some(sender.request) + } + /// Notifies receiver about the event happening in this queue. - fn notify(&self, message: QueueEvent) { + fn notify_message(&self, message: QueueEvent) { for listener in &*self.on_event.read() { listener(message.clone()) } } - - /// Removes requests from this queue and notifies `ConfirmationReceiver` holder about the result. - /// Notifies also a receiver about that event. - fn remove(&self, id: U256, result: Option) -> Option { - let sender = self.queue.write().remove(&id); - - if let Some(sender) = sender { - // notify receiver about the event - self.notify(result.clone().map_or_else( - || QueueEvent::RequestRejected(id), - |_| QueueEvent::RequestConfirmed(id) - )); - - // notify confirmation receiver about resolution - let result = result.ok_or(errors::request_rejected()); - sender.sender.send(result); - - Some(sender.request) - } else { - None - } - } } impl Drop for ConfirmationsQueue { @@ -193,22 +190,26 @@ impl SigningQueue for ConfirmationsQueue { (id, receiver) }; // Notify listeners - self.notify(QueueEvent::NewRequest(id)); + self.notify_message(QueueEvent::NewRequest(id)); Ok(res) } - fn peek(&self, id: &U256) -> Option { - self.queue.read().get(id).map(|sender| sender.request.clone()) + fn take(&self, id: &U256) -> Option { + self.queue.write().remove(id) } - fn request_rejected(&self, id: U256) -> Option { - debug!(target: "own_tx", "Signer: Request rejected ({:?}).", id); - self.remove(id, None) + fn request_rejected(&self, sender: ConfirmationSender) -> Option { + debug!(target: "own_tx", "Signer: Request rejected ({:?}).", sender.request.id); + self.notify_result(sender, None) } - fn request_confirmed(&self, id: U256, result: ConfirmationResult) -> Option { - debug!(target: "own_tx", "Signer: Transaction confirmed ({:?}).", id); - self.remove(id, Some(result)) + fn request_confirmed(&self, sender: ConfirmationSender, result: ConfirmationResult) -> Option { + debug!(target: "own_tx", "Signer: Request confirmed ({:?}).", sender.request.id); + self.notify_result(sender, Some(result)) + } + + fn request_untouched(&self, sender: ConfirmationSender) { + self.queue.write().insert(sender.request.id, sender); } fn requests(&self) -> Vec { @@ -227,7 +228,6 @@ impl SigningQueue for ConfirmationsQueue { } } - #[cfg(test)] mod test { use std::sync::Arc; @@ -261,7 +261,8 @@ mod test { // when let (id, future) = queue.add_request(request, Default::default()).unwrap(); - queue.request_confirmed(id, Ok(ConfirmationResponse::SendTransaction(1.into()))); + let sender = queue.take(&id).unwrap(); + queue.request_confirmed(sender, Ok(ConfirmationResponse::SendTransaction(1.into()))); // then let confirmation = future.wait().unwrap(); diff --git a/rpc/src/v1/helpers/subscribers.rs b/rpc/src/v1/helpers/subscribers.rs index 11dd45d11b8f0c2a45ba6bb1bd400ae7582d0cdb..68712076438c9d2cb61333b31dd837e4744a6454 100644 --- a/rpc/src/v1/helpers/subscribers.rs +++ b/rpc/src/v1/helpers/subscribers.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -22,7 +22,6 @@ use jsonrpc_macros::pubsub::{Subscriber, Sink, SubscriptionId}; use rand::{Rng, StdRng}; use v1::types::H64; - #[derive(Debug, Clone, Hash, Eq, PartialEq)] pub struct Id(H64); impl str::FromStr for Id { diff --git a/rpc/src/v1/helpers/subscription_manager.rs b/rpc/src/v1/helpers/subscription_manager.rs index 5988824b6a4e63f47e5a3533c01aa050978d7b01..5f6d77d883088906b98240392005f3dd667e5437 100644 --- a/rpc/src/v1/helpers/subscription_manager.rs +++ b/rpc/src/v1/helpers/subscription_manager.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index 11fe9e341e44e782bdec69f39e82f21d90990cc5..ae7e12cd2841a984d0b6a454a85269c1c566763a 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -20,22 +20,23 @@ use std::thread; use std::time::{Instant, Duration, SystemTime, UNIX_EPOCH}; use std::sync::Arc; -use rlp::{self, UntrustedRlp}; +use rlp::{self, Rlp}; use ethereum_types::{U256, H64, H160, H256, Address}; use parking_lot::Mutex; use ethash::SeedHashCompute; use ethcore::account_provider::{AccountProvider, DappId}; +use ethcore::client::{BlockChainClient, BlockId, TransactionId, UncleId, StateOrBlock, StateClient, StateInfo, Call, EngineInfo, ProvingBlockChainClient}; +// do we need this? use ethcore::block::IsBlock; -use ethcore::client::{MiningBlockChainClient, BlockId, TransactionId, UncleId, StateOrBlock, StateClient, StateInfo, Call, EngineInfo, ProvingBlockChainClient}; use ethcore::ethereum::Ethash; use ethcore::filter::Filter as EthcoreFilter; -use ethcore::header::{BlockNumber as EthBlockNumber, Seal}; +use ethcore::header::{BlockNumber as EthBlockNumber}; use ethcore::log_entry::LogEntry; -use ethcore::miner::MinerService; +use ethcore::miner::{self, MinerService}; use ethcore::snapshot::SnapshotService; use ethcore::encoded; -use ethsync::{SyncProvider}; +use sync::{SyncProvider}; use miner::external::ExternalMinerService; use transaction::{SignedTransaction, LocalizedTransaction}; use hash::keccak; @@ -47,7 +48,6 @@ use jsonrpc_macros::Trailing; use v1::helpers::{errors, limit_logs, fake_sign}; use v1::helpers::dispatch::{FullDispatcher, default_gas_price}; use v1::helpers::block_import::is_major_importing; -use v1::helpers::accounts::unwrap_provider; use v1::traits::Eth; use v1::types::{ RichBlock, Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, @@ -68,6 +68,8 @@ pub struct EthClientOptions { pub send_block_number_in_get_work: bool, /// Gas Price Percentile used as default gas price. pub gas_price_percentile: usize, + /// Set the timeout for the internal poll manager + pub poll_lifetime: u32 } impl EthClientOptions { @@ -86,6 +88,7 @@ impl Default for EthClientOptions { pending_nonce_from_queue: false, allow_pending_receipt_query: true, send_block_number_in_get_work: true, + poll_lifetime: 60u32, gas_price_percentile: 50, } } @@ -93,7 +96,7 @@ impl Default for EthClientOptions { /// Eth rpc implementation. pub struct EthClient where - C: MiningBlockChainClient, + C: miner::BlockChainClient + BlockChainClient, SN: SnapshotService, S: SyncProvider, M: MinerService, @@ -102,7 +105,7 @@ pub struct EthClient where client: Arc, snapshot: Arc, sync: Arc, - accounts: Option>, + accounts: Arc, miner: Arc, external_miner: Arc, seed_compute: Mutex, @@ -110,6 +113,7 @@ pub struct EthClient where eip86_transition: u64, } +#[derive(Debug)] enum BlockNumberOrId { Number(BlockNumber), Id(BlockId), @@ -143,7 +147,7 @@ enum PendingTransactionId { } impl EthClient where - C: MiningBlockChainClient + StateClient + Call + EngineInfo, + C: miner::BlockChainClient + BlockChainClient + StateClient + Call + EngineInfo, SN: SnapshotService, S: SyncProvider, M: MinerService, @@ -154,7 +158,7 @@ impl EthClient, snapshot: &Arc, sync: &Arc, - accounts: &Option>, + accounts: &Arc, miner: &Arc, em: &Arc, options: EthClientOptions @@ -172,35 +176,38 @@ impl EthClient`, errors if provider was not - /// set. - fn account_provider(&self) -> Result> { - unwrap_provider(&self.accounts) - } - fn rich_block(&self, id: BlockNumberOrId, include_txs: bool) -> Result> { let client = &self.client; - let client_query = |id| (client.block(id), client.block_total_difficulty(id), client.block_extra_info(id)); + let client_query = |id| (client.block(id), client.block_total_difficulty(id), client.block_extra_info(id), false); - let (block, difficulty, extra) = match id { + let (block, difficulty, extra, is_pending) = match id { BlockNumberOrId::Number(BlockNumber::Pending) => { let info = self.client.chain_info(); - let pending_block = self.miner.pending_block(info.best_block_number); - let difficulty = { - let latest_difficulty = self.client.block_total_difficulty(BlockId::Latest).expect("blocks in chain have details; qed"); - let pending_difficulty = self.miner.pending_block_header(info.best_block_number).map(|header| *header.difficulty()); + match self.miner.pending_block(info.best_block_number) { + Some(pending_block) => { + warn!("`Pending` is deprecated and may be removed in future versions."); - if let Some(difficulty) = pending_difficulty { - difficulty + latest_difficulty - } else { - latest_difficulty - } - }; + let difficulty = { + let latest_difficulty = self.client.block_total_difficulty(BlockId::Latest).expect("blocks in chain have details; qed"); + let pending_difficulty = self.miner.pending_block_header(info.best_block_number).map(|header| *header.difficulty()); - let extra = pending_block.as_ref().map(|b| self.client.engine().extra_info(&b.header)); + if let Some(difficulty) = pending_difficulty { + difficulty + latest_difficulty + } else { + latest_difficulty + } + }; - (pending_block.map(|b| encoded::Block::new(b.rlp_bytes(Seal::Without))), Some(difficulty), extra) + let extra = self.client.engine().extra_info(&pending_block.header); + + (Some(encoded::Block::new(pending_block.rlp_bytes())), Some(difficulty), Some(extra), true) + }, + None => { + warn!("`Pending` is deprecated and may be removed in future versions. Falling back to `Latest`"); + client_query(BlockId::Latest) + } + } }, BlockNumberOrId::Number(num) => { @@ -208,7 +215,7 @@ impl EthClient BlockId::Latest, BlockNumber::Earliest => BlockId::Earliest, BlockNumber::Num(n) => BlockId::Number(n), - BlockNumber::Pending => unreachable!(), // Already covered + BlockNumber::Pending => unreachable!() // Already covered }; client_query(id) @@ -222,7 +229,10 @@ impl EthClient None, + false => Some(view.hash().into()), + }, size: Some(block.rlp().as_raw().len().into()), parent_hash: view.parent_hash().into(), uncles_hash: view.uncles_hash().into(), @@ -231,10 +241,16 @@ impl EthClient None, + false => Some(view.number().into()), + }, gas_used: view.gas_used().into(), gas_limit: view.gas_limit().into(), - logs_bloom: view.log_bloom().into(), + logs_bloom: match is_pending { + true => None, + false => Some(view.log_bloom().into()), + }, timestamp: view.timestamp().into(), difficulty: view.difficulty().into(), total_difficulty: Some(total_difficulty.into()), @@ -336,7 +352,10 @@ impl EthClient hdr.decode(), + Some(hdr) => match hdr.decode() { + Ok(h) => h, + Err(e) => return Err(errors::decode(e)) + }, None => { return Ok(None); } }; @@ -369,7 +388,7 @@ impl EthClient EthClient Result> { - let store = self.account_provider()?; - store + self.accounts .note_dapp_used(dapp.clone()) - .and_then(|_| store.dapp_addresses(dapp)) + .and_then(|_| self.accounts.dapp_addresses(dapp)) .map_err(|e| errors::account("Could not fetch accounts.", e)) } @@ -412,7 +430,7 @@ impl EthClient(miner: &M, best_block: EthBlockNumber, filter: &EthcoreFilter) -> Vec where M: MinerService { - let receipts = miner.pending_receipts(best_block); + let receipts = miner.pending_receipts(best_block).unwrap_or_default(); let pending_logs = receipts.into_iter() .flat_map(|(hash, r)| r.logs.into_iter().map(|l| (hash.clone(), l)).collect::>()) @@ -430,7 +448,7 @@ pub fn pending_logs(miner: &M, best_block: EthBlockNumber, filter: &EthcoreFi result } -fn check_known(client: &C, number: BlockNumber) -> Result<()> where C: MiningBlockChainClient { +fn check_known(client: &C, number: BlockNumber) -> Result<()> where C: BlockChainClient { use ethcore::block_status::BlockStatus; let id = match number { @@ -450,7 +468,7 @@ fn check_known(client: &C, number: BlockNumber) -> Result<()> where C: Mining const MAX_QUEUE_SIZE_TO_MINE_ON: usize = 4; // because uncles go back 6. impl Eth for EthClient where - C: MiningBlockChainClient + StateClient + ProvingBlockChainClient + Call + EngineInfo + 'static, + C: miner::BlockChainClient + BlockChainClient + StateClient + Call + EngineInfo + 'static, SN: SnapshotService + 'static, S: SyncProvider + 'static, M: MinerService + 'static, @@ -476,7 +494,6 @@ impl Eth for EthClient< _ => (false, None, None), }; - if warping || is_major_importing(Some(status.state), client.queue_info()) { let chain_info = client.chain_info(); let current_block = U256::from(chain_info.best_block_number); @@ -498,7 +515,7 @@ impl Eth for EthClient< fn author(&self, meta: Metadata) -> Result { let dapp = meta.dapp_id(); - let mut miner = self.miner.author(); + let mut miner = self.miner.authoring_params().author; if miner == 0.into() { miner = self.dapp_accounts(dapp.into())?.get(0).cloned().unwrap_or_default(); } @@ -611,16 +628,8 @@ impl Eth for EthClient< let res = match num.unwrap_or_default() { BlockNumber::Pending if self.options.pending_nonce_from_queue => { - let nonce = self.miner.last_nonce(&address) - .map(|n| n + 1.into()) - .or_else(|| self.client.nonce(&address, BlockId::Latest)); - - match nonce { - Some(nonce) => Ok(nonce.into()), - None => Err(errors::database("latest nonce missing")) - } - }, - + Ok(self.miner.next_nonce(&*self.client, &address).into()) + } BlockNumber::Pending => { let info = self.client.chain_info(); let nonce = self.miner @@ -636,7 +645,6 @@ impl Eth for EthClient< None => Err(errors::database("latest nonce missing")) } }, - number => { try_bf!(check_known(&*self.client, number.clone())); match self.client.nonce(&address, block_number_to_id(number)) { @@ -655,13 +663,13 @@ impl Eth for EthClient< } fn block_transaction_count_by_number(&self, num: BlockNumber) -> BoxFuture> { + let block_number = self.client.chain_info().best_block_number; + Box::new(future::ok(match num { - BlockNumber::Pending => Some( - self.miner.status().transactions_in_pending_block.into() - ), + BlockNumber::Pending => + self.miner.pending_transactions(block_number).map(|x| x.len().into()), _ => - self.client.block(block_number_to_id(num)) - .map(|block| block.transactions_count().into()) + self.client.block(block_number_to_id(num)).map(|block| block.transactions_count().into()) })) } @@ -705,8 +713,8 @@ impl Eth for EthClient< let hash: H256 = hash.into(); let block_number = self.client.chain_info().best_block_number; let tx = try_bf!(self.transaction(PendingTransactionId::Hash(hash))).or_else(|| { - self.miner.transaction(block_number, &hash) - .map(|t| Transaction::from_pending(t, block_number, self.eip86_transition)) + self.miner.transaction(&hash) + .map(|t| Transaction::from_pending(t.pending().clone(), block_number + 1, self.eip86_transition)) }); Box::new(future::ok(tx)) @@ -785,11 +793,6 @@ impl Eth for EthClient< } fn work(&self, no_new_work_timeout: Trailing) -> Result { - if !self.miner.can_produce_work_package() { - warn!(target: "miner", "Cannot give work package - engine seals internally."); - return Err(errors::no_work_required()) - } - let no_new_work_timeout = no_new_work_timeout.unwrap_or_default(); // check if we're still syncing and return empty strings in that case @@ -808,50 +811,58 @@ impl Eth for EthClient< } } - if self.miner.author().is_zero() { + if self.miner.authoring_params().author.is_zero() { warn!(target: "miner", "Cannot give work package - no author is configured. Use --author to configure!"); return Err(errors::no_author()) } - self.miner.map_sealing_work(&*self.client, |b| { - let pow_hash = b.hash(); - let target = Ethash::difficulty_to_boundary(b.block().header().difficulty()); - let seed_hash = self.seed_compute.lock().hash_block_number(b.block().header().number()); - - let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs(); - if no_new_work_timeout > 0 && b.block().header().timestamp() + no_new_work_timeout < now { - Err(errors::no_new_work()) - } else if self.options.send_block_number_in_get_work { - let block_number = b.block().header().number(); - Ok(Work { - pow_hash: pow_hash.into(), - seed_hash: seed_hash.into(), - target: target.into(), - number: Some(block_number), - }) - } else { - Ok(Work { - pow_hash: pow_hash.into(), - seed_hash: seed_hash.into(), - target: target.into(), - number: None - }) - } - }).unwrap_or(Err(errors::internal("No work found.", ""))) - } - fn submit_work(&self, nonce: RpcH64, pow_hash: RpcH256, mix_hash: RpcH256) -> Result { - if !self.miner.can_produce_work_package() { - warn!(target: "miner", "Cannot submit work - engine seals internally."); - return Err(errors::no_work_required()) + let work = self.miner.work_package(&*self.client).ok_or_else(|| { + warn!(target: "miner", "Cannot give work package - engine seals internally."); + errors::no_work_required() + })?; + + let (pow_hash, number, timestamp, difficulty) = work; + let target = Ethash::difficulty_to_boundary(&difficulty); + let seed_hash = self.seed_compute.lock().hash_block_number(number); + + let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs(); + if no_new_work_timeout > 0 && timestamp + no_new_work_timeout < now { + Err(errors::no_new_work()) + } else if self.options.send_block_number_in_get_work { + Ok(Work { + pow_hash: pow_hash.into(), + seed_hash: seed_hash.into(), + target: target.into(), + number: Some(number), + }) + } else { + Ok(Work { + pow_hash: pow_hash.into(), + seed_hash: seed_hash.into(), + target: target.into(), + number: None + }) } + } + fn submit_work(&self, nonce: RpcH64, pow_hash: RpcH256, mix_hash: RpcH256) -> Result { + // TODO [ToDr] Should disallow submissions in case of PoA? let nonce: H64 = nonce.into(); let pow_hash: H256 = pow_hash.into(); let mix_hash: H256 = mix_hash.into(); trace!(target: "miner", "submit_work: Decoded: nonce={}, pow_hash={}, mix_hash={}", nonce, pow_hash, mix_hash); let seal = vec![rlp::encode(&mix_hash).into_vec(), rlp::encode(&nonce).into_vec()]; - Ok(self.miner.submit_seal(&*self.client, pow_hash, seal).is_ok()) + let import = self.miner.submit_seal(pow_hash, seal) + .and_then(|block| self.client.import_sealed_block(block)); + + match import { + Ok(_) => Ok(true), + Err(err) => { + warn!(target: "miner", "Cannot submit work - {:?}.", err); + Ok(false) + }, + } } fn submit_hashrate(&self, rate: RpcU256, id: RpcH256) -> Result { @@ -860,7 +871,7 @@ impl Eth for EthClient< } fn send_raw_transaction(&self, raw: Bytes) -> Result { - UntrustedRlp::new(&raw.into_vec()).as_val() + Rlp::new(&raw.into_vec()).as_val() .map_err(errors::rlp) .and_then(|tx| SignedTransaction::new(tx).map_err(errors::transaction)) .and_then(|signed_transaction| { @@ -868,6 +879,7 @@ impl Eth for EthClient< &*self.client, &*self.miner, signed_transaction.into(), + false ) }) .map(Into::into) @@ -898,16 +910,22 @@ impl Eth for EthClient< }; let state = try_bf!(self.client.state_at(id).ok_or(errors::state_pruned())); - let header = try_bf!(self.client.block_header(id).ok_or(errors::state_pruned())); + let header = try_bf!(self.client.block_header(id).ok_or(errors::state_pruned()).and_then(|h| h.decode().map_err(errors::decode))); - (state, header.decode()) + (state, header) }; let result = self.client.call(&signed, Default::default(), &mut state, &header); Box::new(future::done(result - .map(|b| b.output.into()) .map_err(errors::call) + .and_then(|executed| { + match executed.exception { + Some(ref exception) => Err(errors::vm(exception, &executed.output)), + None => Ok(executed) + } + }) + .map(|b| b.output.into()) )) } @@ -931,9 +949,9 @@ impl Eth for EthClient< }; let state = try_bf!(self.client.state_at(id).ok_or(errors::state_pruned())); - let header = try_bf!(self.client.block_header(id).ok_or(errors::state_pruned())); + let header = try_bf!(self.client.block_header(id).ok_or(errors::state_pruned()).and_then(|h| h.decode().map_err(errors::decode))); - (state, header.decode()) + (state, header) }; Box::new(future::done(self.client.estimate_gas(&signed, &state, &header) diff --git a/rpc/src/v1/impls/eth_filter.rs b/rpc/src/v1/impls/eth_filter.rs index 0ff9f4c20f42a38c69b7adf32ab133f94d0d39a0..b79456cd8dbb2f898fc3680ceea436ce83831ea1 100644 --- a/rpc/src/v1/impls/eth_filter.rs +++ b/rpc/src/v1/impls/eth_filter.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -19,7 +19,7 @@ use std::sync::Arc; use std::collections::HashSet; -use ethcore::miner::MinerService; +use ethcore::miner::{self, MinerService}; use ethcore::filter::Filter as EthcoreFilter; use ethcore::client::{BlockChainClient, BlockId}; use ethereum_types::H256; @@ -30,7 +30,7 @@ use jsonrpc_core::futures::{future, Future}; use jsonrpc_core::futures::future::Either; use v1::traits::EthFilter; use v1::types::{BlockNumber, Index, Filter, FilterChanges, Log, H256 as RpcH256, U256 as RpcU256}; -use v1::helpers::{PollFilter, PollManager, limit_logs}; +use v1::helpers::{errors, PollFilter, PollManager, limit_logs}; use v1::impls::eth::pending_logs; /// Something which provides data that can be filtered over. @@ -39,10 +39,10 @@ pub trait Filterable { fn best_block_number(&self) -> u64; /// Get a block hash by block id. - fn block_hash(&self, id: BlockId) -> Option; + fn block_hash(&self, id: BlockId) -> Option; /// pending transaction hashes at the given block. - fn pending_transactions_hashes(&self, block_number: u64) -> Vec; + fn pending_transactions_hashes(&self) -> Vec; /// Get logs that match the given filter. fn logs(&self, filter: EthcoreFilter) -> BoxFuture>; @@ -52,40 +52,46 @@ pub trait Filterable { /// Get a reference to the poll manager. fn polls(&self) -> &Mutex>; + + /// Get removed logs within route from the given block to the nearest canon block, not including the canon block. Also returns how many logs have been traversed. + fn removed_logs(&self, block_hash: H256, filter: &EthcoreFilter) -> (Vec, u64); } /// Eth filter rpc implementation for a full node. -pub struct EthFilterClient where - C: BlockChainClient, - M: MinerService { - +pub struct EthFilterClient { client: Arc, miner: Arc, polls: Mutex>, } -impl EthFilterClient where C: BlockChainClient, M: MinerService { +impl EthFilterClient { /// Creates new Eth filter client. - pub fn new(client: Arc, miner: Arc) -> Self { + pub fn new(client: Arc, miner: Arc, poll_lifetime: u32) -> Self { EthFilterClient { client: client, miner: miner, - polls: Mutex::new(PollManager::new()), + polls: Mutex::new(PollManager::new(poll_lifetime)), } } } -impl Filterable for EthFilterClient where C: BlockChainClient, M: MinerService { +impl Filterable for EthFilterClient where + C: miner::BlockChainClient + BlockChainClient, + M: MinerService, +{ fn best_block_number(&self) -> u64 { self.client.chain_info().best_block_number } - fn block_hash(&self, id: BlockId) -> Option { - self.client.block_hash(id).map(Into::into) + fn block_hash(&self, id: BlockId) -> Option { + self.client.block_hash(id) } - fn pending_transactions_hashes(&self, best: u64) -> Vec { - self.miner.pending_transactions_hashes(best) + fn pending_transactions_hashes(&self) -> Vec { + self.miner.ready_transactions(&*self.client, usize::max_value(), miner::PendingOrdering::Priority) + .into_iter() + .map(|tx| tx.signed().hash()) + .collect() } fn logs(&self, filter: EthcoreFilter) -> BoxFuture> { @@ -97,15 +103,47 @@ impl Filterable for EthFilterClient where C: BlockChainClient, M: Mi } fn polls(&self) -> &Mutex> { &self.polls } -} + fn removed_logs(&self, block_hash: H256, filter: &EthcoreFilter) -> (Vec, u64) { + let inner = || -> Option> { + let mut route = Vec::new(); + + let mut current_block_hash = block_hash; + let mut current_block_header = self.client.block_header(BlockId::Hash(current_block_hash))?; + + while current_block_hash != self.client.block_hash(BlockId::Number(current_block_header.number()))? { + route.push(current_block_hash); + + current_block_hash = current_block_header.parent_hash(); + current_block_header = self.client.block_header(BlockId::Hash(current_block_hash))?; + } + + Some(route) + }; + let route = inner().unwrap_or_default(); + let route_len = route.len() as u64; + (route.into_iter().flat_map(|block_hash| { + let mut filter = filter.clone(); + filter.from_block = BlockId::Hash(block_hash); + filter.to_block = filter.from_block; + + self.client.logs(filter).into_iter().map(|log| { + let mut log: Log = log.into(); + log.log_type = "removed".into(); + log.removed = true; + + log + }) + }).collect(), route_len) + } +} impl EthFilter for T { fn new_filter(&self, filter: Filter) -> Result { let mut polls = self.polls().lock(); let block_number = self.best_block_number(); - let id = polls.create_poll(PollFilter::Logs(block_number, Default::default(), filter)); + let id = polls.create_poll(PollFilter::Logs(block_number, None, Default::default(), filter)); Ok(id.into()) } @@ -118,8 +156,7 @@ impl EthFilter for T { fn new_pending_transaction_filter(&self) -> Result { let mut polls = self.polls().lock(); - let best_block = self.best_block_number(); - let pending_transactions = self.pending_transactions_hashes(best_block); + let pending_transactions = self.pending_transactions_hashes(); let id = polls.create_poll(PollFilter::PendingTransaction(pending_transactions)); Ok(id.into()) } @@ -127,14 +164,14 @@ impl EthFilter for T { fn filter_changes(&self, index: Index) -> BoxFuture { let mut polls = self.polls().lock(); Box::new(match polls.poll_mut(&index.value()) { - None => Either::A(future::ok(FilterChanges::Empty)), + None => Either::A(future::err(errors::filter_not_found())), Some(filter) => match *filter { PollFilter::Block(ref mut block_number) => { // +1, cause we want to return hashes including current block hash. let current_number = self.best_block_number() + 1; let hashes = (*block_number..current_number).into_iter() .map(BlockId::Number) - .filter_map(|id| self.block_hash(id)) + .filter_map(|id| self.block_hash(id).map(Into::into)) .collect::>(); *block_number = current_number; @@ -143,8 +180,7 @@ impl EthFilter for T { }, PollFilter::PendingTransaction(ref mut previous_hashes) => { // get hashes of pending transactions - let best_block = self.best_block_number(); - let current_hashes = self.pending_transactions_hashes(best_block); + let current_hashes = self.pending_transactions_hashes(); let new_hashes = { @@ -165,7 +201,7 @@ impl EthFilter for T { // return new hashes Either::A(future::ok(FilterChanges::Hashes(new_hashes))) }, - PollFilter::Logs(ref mut block_number, ref mut previous_logs, ref filter) => { + PollFilter::Logs(ref mut block_number, ref mut last_block_hash, ref mut previous_logs, ref filter) => { // retrive the current block number let current_number = self.best_block_number(); @@ -174,6 +210,11 @@ impl EthFilter for T { // build appropriate filter let mut filter: EthcoreFilter = filter.clone().into(); + + // retrieve reorg logs + let (mut reorg, reorg_len) = last_block_hash.map_or_else(|| (Vec::new(), 0), |h| self.removed_logs(h, &filter)); + *block_number -= reorg_len as u64; + filter.from_block = BlockId::Number(*block_number); filter.to_block = BlockId::Latest; @@ -199,9 +240,14 @@ impl EthFilter for T { // we want to get logs *block_number = current_number + 1; + // save the current block hash, which we used to get back to the + // canon chain in case of reorg. + *last_block_hash = self.block_hash(BlockId::Number(current_number)); + // retrieve logs in range from_block..min(BlockId::Latest..to_block) let limit = filter.limit; Either::B(self.logs(filter) + .map(move |logs| { reorg.extend(logs); reorg }) // append reorg logs in the front .map(move |mut logs| { logs.extend(pending); logs }) // append fetched pending logs .map(move |logs| limit_logs(logs, limit)) // limit the logs .map(FilterChanges::Logs)) @@ -215,9 +261,10 @@ impl EthFilter for T { let mut polls = self.polls().lock(); match polls.poll(&index.value()) { - Some(&PollFilter::Logs(ref _block_number, ref _previous_log, ref filter)) => filter.clone(), + Some(&PollFilter::Logs(ref _block_number, ref _last_block_hash, ref _previous_log, ref filter)) => filter.clone(), // just empty array - _ => return Box::new(future::ok(Vec::new())), + Some(_) => return Box::new(future::ok(Vec::new())), + None => return Box::new(future::err(errors::filter_not_found())), } }; @@ -242,7 +289,6 @@ impl EthFilter for T { } fn uninstall_filter(&self, index: Index) -> Result { - self.polls().lock().remove_poll(&index.value()); - Ok(true) + Ok(self.polls().lock().remove_poll(&index.value())) } } diff --git a/rpc/src/v1/impls/eth_pubsub.rs b/rpc/src/v1/impls/eth_pubsub.rs index 315fc2f4f896da476e3f7c3e2a72734813a3fc22..9f592b1fa79eb183a86e8254b7a2ff79bd1d37b5 100644 --- a/rpc/src/v1/impls/eth_pubsub.rs +++ b/rpc/src/v1/impls/eth_pubsub.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -18,6 +18,7 @@ use std::sync::{Arc, Weak}; use std::collections::BTreeMap; +use std::time::Duration; use jsonrpc_core::{BoxFuture, Result, Error}; use jsonrpc_core::futures::{self, Future, IntoFuture}; @@ -33,8 +34,8 @@ use v1::types::{pubsub, RichHeader, Log}; use ethcore::encoded; use ethcore::filter::Filter as EthFilter; -use ethcore::client::{BlockChainClient, ChainNotify, BlockId}; -use ethsync::LightSync; +use ethcore::client::{BlockChainClient, ChainNotify, ChainRoute, ChainRouteType, BlockId}; +use sync::LightSync; use light::cache::Cache; use light::on_demand::OnDemand; use light::client::{LightChainClient, LightChainNotify}; @@ -140,19 +141,20 @@ impl ChainNotificationHandler { } } - fn notify_logs(&self, enacted: &[H256], logs: F) where - F: Fn(EthFilter) -> T, + fn notify_logs(&self, enacted: &[(H256, Ex)], logs: F) where + F: Fn(EthFilter, &Ex) -> T, + Ex: Send, T: IntoFuture, Error = Error>, T::Future: Send + 'static, { for &(ref subscriber, ref filter) in self.logs_subscribers.read().values() { let logs = futures::future::join_all(enacted .iter() - .map(|hash| { + .map(|&(hash, ref ex)| { let mut filter = filter.clone(); - filter.from_block = BlockId::Hash(*hash); + filter.from_block = BlockId::Hash(hash); filter.to_block = filter.from_block.clone(); - logs(filter).into_future() + logs(filter, ex).into_future() }) .collect::>() ); @@ -173,7 +175,7 @@ impl ChainNotificationHandler { } /// Notify all subscribers about new transaction hashes. - pub fn new_transactions(&self, hashes: &[H256]) { + pub fn notify_new_transactions(&self, hashes: &[H256]) { for subscriber in self.transactions_subscribers.read().values() { for hash in hashes { Self::notify(&self.remote, subscriber, pubsub::Result::TransactionHash((*hash).into())); @@ -197,7 +199,7 @@ impl LightClient for LightFetch { } fn logs(&self, filter: EthFilter) -> BoxFuture> { - LightFetch::logs(self, filter) + Box::new(LightFetch::logs(self, filter)) as BoxFuture<_> } } @@ -213,7 +215,7 @@ impl LightChainNotify for ChainNotificationHandler { .collect::>(); self.notify_heads(&headers); - self.notify_logs(&enacted, |filter| self.client.logs(filter)) + self.notify_logs(&enacted.iter().map(|h| (*h, ())).collect::>(), |filter, _| self.client.logs(filter)) } } @@ -222,17 +224,21 @@ impl ChainNotify for ChainNotificationHandler { &self, _imported: Vec, _invalid: Vec, - enacted: Vec, - retracted: Vec, + route: ChainRoute, _sealed: Vec, // Block bytes. _proposed: Vec, - _duration: u64, + _duration: Duration, ) { const EXTRA_INFO_PROOF: &'static str = "Object exists in in blockchain (fetched earlier), extra_info is always available if object exists; qed"; - let headers = enacted + let headers = route.route() .iter() - .filter_map(|hash| self.client.block_header(BlockId::Hash(*hash))) + .filter_map(|&(hash, ref typ)| { + match typ { + &ChainRouteType::Retracted => None, + &ChainRouteType::Enacted => self.client.block_header(BlockId::Hash(hash)) + } + }) .map(|header| { let hash = header.hash(); (header, self.client.block_extra_info(BlockId::Hash(hash)).expect(EXTRA_INFO_PROOF)) @@ -242,17 +248,18 @@ impl ChainNotify for ChainNotificationHandler { // Headers self.notify_heads(&headers); - // Enacted logs - self.notify_logs(&enacted, |filter| { - Ok(self.client.logs(filter).into_iter().map(Into::into).collect()) - }); - - // Retracted logs - self.notify_logs(&retracted, |filter| { - Ok(self.client.logs(filter).into_iter().map(Into::into).map(|mut log: Log| { - log.log_type = "removed".into(); - log - }).collect()) + // We notify logs enacting and retracting as the order in route. + self.notify_logs(route.route(), |filter, ex| { + match ex { + &ChainRouteType::Enacted => + Ok(self.client.logs(filter).into_iter().map(Into::into).collect()), + &ChainRouteType::Retracted => + Ok(self.client.logs(filter).into_iter().map(Into::into).map(|mut log: Log| { + log.log_type = "removed".into(); + log.removed = true; + log + }).collect()), + } }); } } diff --git a/rpc/src/v1/impls/light/eth.rs b/rpc/src/v1/impls/light/eth.rs index 8d7ad736bbbba8566dd9f8d554a174af395a99e4..b87cf7f0adb1633c7bfc825cf0834cb69818b477 100644 --- a/rpc/src/v1/impls/light/eth.rs +++ b/rpc/src/v1/impls/light/eth.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -32,11 +32,11 @@ use ethcore::account_provider::{AccountProvider, DappId}; use ethcore::encoded; use ethcore::filter::Filter as EthcoreFilter; use ethcore::ids::BlockId; -use ethsync::LightSync; +use sync::LightSync; use hash::{KECCAK_NULL_RLP, KECCAK_EMPTY_LIST_RLP}; use ethereum_types::U256; use parking_lot::{RwLock, Mutex}; -use rlp::UntrustedRlp; +use rlp::Rlp; use transaction::SignedTransaction; use v1::impls::eth_filter::Filterable; @@ -62,6 +62,7 @@ pub struct EthClient { accounts: Arc, cache: Arc>, polls: Mutex>, + poll_lifetime: u32, gas_price_percentile: usize, } @@ -92,7 +93,8 @@ impl Clone for EthClient { transaction_queue: self.transaction_queue.clone(), accounts: self.accounts.clone(), cache: self.cache.clone(), - polls: Mutex::new(PollManager::new()), + polls: Mutex::new(PollManager::new(self.poll_lifetime)), + poll_lifetime: self.poll_lifetime, gas_price_percentile: self.gas_price_percentile, } } @@ -109,6 +111,7 @@ impl EthClient { accounts: Arc, cache: Arc>, gas_price_percentile: usize, + poll_lifetime: u32 ) -> Self { EthClient { sync, @@ -117,7 +120,8 @@ impl EthClient { transaction_queue, accounts, cache, - polls: Mutex::new(PollManager::new()), + polls: Mutex::new(PollManager::new(poll_lifetime)), + poll_lifetime, gas_price_percentile, } } @@ -157,7 +161,7 @@ impl EthClient { number: Some(header.number().into()), gas_used: header.gas_used().clone().into(), gas_limit: header.gas_limit().clone().into(), - logs_bloom: header.log_bloom().clone().into(), + logs_bloom: Some(header.log_bloom().clone().into()), timestamp: header.timestamp().into(), difficulty: header.difficulty().clone().into(), total_difficulty: score.map(Into::into), @@ -371,9 +375,9 @@ impl Eth for EthClient { } fn send_raw_transaction(&self, raw: Bytes) -> Result { - let best_header = self.client.best_block_header().decode(); + let best_header = self.client.best_block_header().decode().map_err(errors::decode)?; - UntrustedRlp::new(&raw.into_vec()).as_val() + Rlp::new(&raw.into_vec()).as_val() .map_err(errors::rlp) .and_then(|tx| { self.client.engine().verify_transaction_basic(&tx, &best_header) @@ -535,16 +539,16 @@ impl Eth for EthClient { impl Filterable for EthClient { fn best_block_number(&self) -> u64 { self.client.chain_info().best_block_number } - fn block_hash(&self, id: BlockId) -> Option { - self.client.block_hash(id).map(Into::into) + fn block_hash(&self, id: BlockId) -> Option<::ethereum_types::H256> { + self.client.block_hash(id) } - fn pending_transactions_hashes(&self, _block_number: u64) -> Vec<::ethereum_types::H256> { + fn pending_transactions_hashes(&self) -> Vec<::ethereum_types::H256> { Vec::new() } fn logs(&self, filter: EthcoreFilter) -> BoxFuture> { - self.fetcher().logs(filter) + Box::new(self.fetcher().logs(filter)) as BoxFuture<_> } fn pending_logs(&self, _block_number: u64, _filter: &EthcoreFilter) -> Vec { @@ -554,6 +558,10 @@ impl Filterable for EthClient { fn polls(&self) -> &Mutex> { &self.polls } + + fn removed_logs(&self, _block_hash: ::ethereum_types::H256, _filter: &EthcoreFilter) -> (Vec, u64) { + (Default::default(), 0) + } } fn extract_uncle_at_index(block: encoded::Block, index: Index, client: Arc) -> Option { @@ -576,7 +584,7 @@ fn extract_uncle_at_index(block: encoded::Block, index: Ind number: Some(uncle.number().into()), gas_used: uncle.gas_used().clone().into(), gas_limit: uncle.gas_limit().clone().into(), - logs_bloom: uncle.log_bloom().clone().into(), + logs_bloom: Some(uncle.log_bloom().clone().into()), timestamp: uncle.timestamp().into(), difficulty: uncle.difficulty().clone().into(), total_difficulty: None, diff --git a/rpc/src/v1/impls/light/mod.rs b/rpc/src/v1/impls/light/mod.rs index 38ba2438e24ee49c9349ed571fd78efdb54d963e..40f1df89907b46f05da3a7eacfe722d2f03f662f 100644 --- a/rpc/src/v1/impls/light/mod.rs +++ b/rpc/src/v1/impls/light/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/impls/light/net.rs b/rpc/src/v1/impls/light/net.rs index 3491b35ba77260edc7ff35dddccef124205bc794..4dbc9d1908534952a9b8617cabc6634bba5a52ce 100644 --- a/rpc/src/v1/impls/light/net.rs +++ b/rpc/src/v1/impls/light/net.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -17,7 +17,7 @@ //! Net rpc implementation. use std::sync::Arc; use jsonrpc_core::Result; -use ethsync::LightSyncProvider; +use sync::LightSyncProvider; use v1::traits::Net; /// Net rpc implementation. diff --git a/rpc/src/v1/impls/light/parity.rs b/rpc/src/v1/impls/light/parity.rs index 3b2fab9a3cbf14b5f98e9c0cdf4f9806402bfc8e..6e93132b92212f0b8d5f173a7bc3e38f68fdfa36 100644 --- a/rpc/src/v1/impls/light/parity.rs +++ b/rpc/src/v1/impls/light/parity.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -20,10 +20,10 @@ use std::collections::{BTreeMap, HashSet}; use version::version_data; -use crypto::{ecies, DEFAULT_MAC}; -use ethkey::{Brain, Generator}; +use crypto::DEFAULT_MAC; +use ethkey::{crypto::ecies, Brain, Generator}; use ethstore::random_phrase; -use ethsync::LightSyncProvider; +use sync::LightSyncProvider; use ethcore::account_provider::AccountProvider; use ethcore_logger::RotatingLogger; use node_health::{NodeHealth, Health}; @@ -264,12 +264,28 @@ impl Parity for ParityClient { .map(Into::into) } - fn pending_transactions(&self) -> Result> { + fn pending_transactions(&self, limit: Trailing) -> Result> { let txq = self.light_dispatch.transaction_queue.read(); let chain_info = self.light_dispatch.client.chain_info(); Ok( txq.ready_transactions(chain_info.best_block_number, chain_info.best_block_timestamp) .into_iter() + .take(limit.unwrap_or_else(usize::max_value)) + .map(|tx| Transaction::from_pending(tx, chain_info.best_block_number, self.eip86_transition)) + .collect::>() + ) + } + + fn all_transactions(&self) -> Result> { + let txq = self.light_dispatch.transaction_queue.read(); + let chain_info = self.light_dispatch.client.chain_info(); + + let current = txq.ready_transactions(chain_info.best_block_number, chain_info.best_block_timestamp); + let future = txq.future_transactions(chain_info.best_block_number, chain_info.best_block_timestamp); + Ok( + current + .into_iter() + .chain(future.into_iter()) .map(|tx| Transaction::from_pending(tx, chain_info.best_block_number, self.eip86_transition)) .collect::>() ) @@ -380,9 +396,9 @@ impl Parity for ParityClient { let engine = self.light_dispatch.client.engine().clone(); let from_encoded = move |encoded: encoded::Header| { - let header = encoded.decode(); + let header = encoded.decode().map_err(errors::decode)?; let extra_info = engine.extra_info(&header); - RichHeader { + Ok(RichHeader { inner: Header { hash: Some(header.hash().into()), size: Some(encoded.rlp().as_raw().len().into()), @@ -403,9 +419,8 @@ impl Parity for ParityClient { extra_data: Bytes::new(header.extra_data().clone()), }, extra_info: extra_info, - } + }) }; - // Note: Here we treat `Pending` as `Latest`. // Since light clients don't produce pending blocks // (they don't have state) we can safely fallback to `Latest`. @@ -415,7 +430,7 @@ impl Parity for ParityClient { BlockNumber::Latest | BlockNumber::Pending => BlockId::Latest, }; - Box::new(self.fetcher().header(id).map(from_encoded)) + Box::new(self.fetcher().header(id).and_then(from_encoded)) } fn ipfs_cid(&self, content: Bytes) -> Result { diff --git a/rpc/src/v1/impls/light/parity_set.rs b/rpc/src/v1/impls/light/parity_set.rs index a1344fa69b95b08388aad7c341c9167d0c478121..4e907deaf199676c1fa12171d0de0b6e53e456a2 100644 --- a/rpc/src/v1/impls/light/parity_set.rs +++ b/rpc/src/v1/impls/light/parity_set.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ use std::io; use std::sync::Arc; -use ethsync::ManageNetwork; +use sync::ManageNetwork; use fetch::{self, Fetch}; use futures_cpupool::CpuPool; use hash::keccak_buffer; @@ -128,7 +128,7 @@ impl ParitySet for ParitySetClient { } fn hash_content(&self, url: String) -> BoxFuture { - let future = self.fetch.fetch(&url, Default::default()).then(move |result| { + let future = self.fetch.get(&url, Default::default()).then(move |result| { result .map_err(errors::fetch) .and_then(move |response| { diff --git a/rpc/src/v1/impls/light/trace.rs b/rpc/src/v1/impls/light/trace.rs index 1d2c7fcaa0ade2609598e33d38ca6a79facdb3ca..d1e99fb9a1d24c67ce57887cff8b4e56822fa0a6 100644 --- a/rpc/src/v1/impls/light/trace.rs +++ b/rpc/src/v1/impls/light/trace.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/impls/mod.rs b/rpc/src/v1/impls/mod.rs index d3e0554c24fcad3844c6a9f668f223b8145875f4..1349147207dbaf624a6ac77a2c617819d3358d9e 100644 --- a/rpc/src/v1/impls/mod.rs +++ b/rpc/src/v1/impls/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -32,6 +32,7 @@ mod rpc; mod secretstore; mod traces; mod web3; +mod private; pub mod light; @@ -51,3 +52,4 @@ pub use self::traces::TracesClient; pub use self::web3::Web3Client; pub use self::rpc::RpcClient; pub use self::secretstore::SecretStoreClient; +pub use self::private::PrivateClient; diff --git a/rpc/src/v1/impls/net.rs b/rpc/src/v1/impls/net.rs index 50dbffdc79ed4f4d7b966e7eec89e04eea99620f..74521d81356e200f34e6c8be025d1ca3bc1828de 100644 --- a/rpc/src/v1/impls/net.rs +++ b/rpc/src/v1/impls/net.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -17,7 +17,7 @@ //! Net rpc implementation. use std::sync::Arc; use jsonrpc_core::Result; -use ethsync::SyncProvider; +use sync::SyncProvider; use v1::traits::Net; /// Net rpc implementation. diff --git a/rpc/src/v1/impls/parity.rs b/rpc/src/v1/impls/parity.rs index 95c810aa1a3eba8ff42a01b221170a7fe3c88233..d7c26014edda998f76fbad1b5158982008cd1907 100644 --- a/rpc/src/v1/impls/parity.rs +++ b/rpc/src/v1/impls/parity.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -22,15 +22,14 @@ use std::collections::{BTreeMap, HashSet}; use ethereum_types::Address; use version::version_data; -use crypto::{DEFAULT_MAC, ecies}; -use ethkey::{Brain, Generator}; +use crypto::DEFAULT_MAC; +use ethkey::{crypto::ecies, Brain, Generator}; use ethstore::random_phrase; -use ethsync::{SyncProvider, ManageNetwork}; +use sync::{SyncProvider, ManageNetwork}; use ethcore::account_provider::AccountProvider; -use ethcore::client::{MiningBlockChainClient, StateClient, Call}; +use ethcore::client::{BlockChainClient, StateClient, Call}; use ethcore::ids::BlockId; -use ethcore::miner::MinerService; -use ethcore::mode::Mode; +use ethcore::miner::{self, MinerService}; use ethcore::state::StateInfo; use ethcore_logger::RotatingLogger; use node_health::{NodeHealth, Health}; @@ -40,7 +39,6 @@ use jsonrpc_core::{BoxFuture, Result}; use jsonrpc_core::futures::{future, Future}; use jsonrpc_macros::Trailing; use v1::helpers::{self, errors, fake_sign, ipfs, SigningQueue, SignerService, NetworkSettings}; -use v1::helpers::accounts::unwrap_provider; use v1::metadata::Metadata; use v1::traits::Parity; use v1::types::{ @@ -62,7 +60,7 @@ pub struct ParityClient { sync: Arc, net: Arc, health: NodeHealth, - accounts: Option>, + accounts: Arc, logger: Arc, settings: Arc, signer: Option>, @@ -72,7 +70,7 @@ pub struct ParityClient { } impl ParityClient where - C: MiningBlockChainClient, + C: BlockChainClient, { /// Creates new `ParityClient`. pub fn new( @@ -82,7 +80,7 @@ impl ParityClient where updater: Arc, net: Arc, health: NodeHealth, - accounts: Option>, + accounts: Arc, logger: Arc, settings: Arc, signer: Option>, @@ -106,17 +104,11 @@ impl ParityClient where eip86_transition, } } - - /// Attempt to get the `Arc`, errors if provider was not - /// set. - fn account_provider(&self) -> Result> { - unwrap_provider(&self.accounts) - } } impl Parity for ParityClient where S: StateInfo + 'static, - C: MiningBlockChainClient + StateClient + Call + 'static, + C: miner::BlockChainClient + BlockChainClient + StateClient + Call + 'static, M: MinerService + 'static, U: UpdateService + 'static, { @@ -125,15 +117,14 @@ impl Parity for ParityClient where fn accounts_info(&self, dapp: Trailing) -> Result> { let dapp = dapp.unwrap_or_default(); - let store = self.account_provider()?; - let dapp_accounts = store + let dapp_accounts = self.accounts .note_dapp_used(dapp.clone().into()) - .and_then(|_| store.dapp_addresses(dapp.into())) + .and_then(|_| self.accounts.dapp_addresses(dapp.into())) .map_err(|e| errors::account("Could not fetch accounts.", e))? .into_iter().collect::>(); - let info = store.accounts_info().map_err(|e| errors::account("Could not fetch account info.", e))?; - let other = store.addresses_info(); + let info = self.accounts.accounts_info().map_err(|e| errors::account("Could not fetch account info.", e))?; + let other = self.accounts.addresses_info(); Ok(info .into_iter() @@ -145,8 +136,7 @@ impl Parity for ParityClient where } fn hardware_accounts_info(&self) -> Result> { - let store = self.account_provider()?; - let info = store.hardware_accounts_info().map_err(|e| errors::account("Could not fetch account info.", e))?; + let info = self.accounts.hardware_accounts_info().map_err(|e| errors::account("Could not fetch account info.", e))?; Ok(info .into_iter() .map(|(a, v)| (H160::from(a), HwAccountInfo { name: v.name, manufacturer: v.meta })) @@ -155,14 +145,13 @@ impl Parity for ParityClient where } fn locked_hardware_accounts_info(&self) -> Result> { - let store = self.account_provider()?; - Ok(store.locked_hardware_accounts().map_err(|e| errors::account("Error communicating with hardware wallet.", e))?) + self.accounts.locked_hardware_accounts().map_err(|e| errors::account("Error communicating with hardware wallet.", e)) } fn default_account(&self, meta: Self::Metadata) -> Result { let dapp_id = meta.dapp_id(); - Ok(self.account_provider()? + Ok(self.accounts .dapp_default_address(dapp_id.into()) .map(Into::into) .ok() @@ -170,23 +159,23 @@ impl Parity for ParityClient where } fn transactions_limit(&self) -> Result { - Ok(self.miner.transactions_limit()) + Ok(self.miner.queue_status().limits.max_count) } fn min_gas_price(&self) -> Result { - Ok(U256::from(self.miner.minimal_gas_price())) + Ok(self.miner.queue_status().options.minimal_gas_price.into()) } fn extra_data(&self) -> Result { - Ok(Bytes::new(self.miner.extra_data())) + Ok(Bytes::new(self.miner.authoring_params().extra_data)) } fn gas_floor_target(&self) -> Result { - Ok(U256::from(self.miner.gas_floor_target())) + Ok(U256::from(self.miner.authoring_params().gas_range_target.0)) } fn gas_ceil_target(&self) -> Result { - Ok(U256::from(self.miner.gas_ceil_target())) + Ok(U256::from(self.miner.authoring_params().gas_range_target.1)) } fn dev_logs(&self) -> Result> { @@ -212,13 +201,14 @@ impl Parity for ParityClient where fn net_peers(&self) -> Result { let sync_status = self.sync.status(); - let net_config = self.net.network_config(); + let num_peers_range = self.net.num_peers_range(); + debug_assert!(num_peers_range.end > num_peers_range.start); let peers = self.sync.peers().into_iter().map(Into::into).collect(); Ok(Peers { active: sync_status.num_active_peers, connected: sync_status.num_peers, - max: sync_status.current_max_peers(net_config.min_peers, net_config.max_peers), + max: sync_status.current_max_peers(num_peers_range.start, num_peers_range.end - 1), peers: peers }) } @@ -313,14 +303,34 @@ impl Parity for ParityClient where .map(Into::into) } - fn pending_transactions(&self) -> Result> { + fn pending_transactions(&self, limit: Trailing) -> Result> { let block_number = self.client.chain_info().best_block_number; - Ok(self.miner.pending_transactions().into_iter().map(|t| Transaction::from_pending(t, block_number, self.eip86_transition)).collect::>()) + let ready_transactions = self.miner.ready_transactions( + &*self.client, + limit.unwrap_or_else(usize::max_value), + miner::PendingOrdering::Priority, + ); + + Ok(ready_transactions + .into_iter() + .map(|t| Transaction::from_pending(t.pending().clone(), block_number, self.eip86_transition)) + .collect() + ) } - fn future_transactions(&self) -> Result> { + fn all_transactions(&self) -> Result> { let block_number = self.client.chain_info().best_block_number; - Ok(self.miner.future_transactions().into_iter().map(|t| Transaction::from_pending(t, block_number, self.eip86_transition)).collect::>()) + let all_transactions = self.miner.queued_transactions(); + + Ok(all_transactions + .into_iter() + .map(|t| Transaction::from_pending(t.pending().clone(), block_number, self.eip86_transition)) + .collect() + ) + } + + fn future_transactions(&self) -> Result> { + Err(errors::deprecated("Use `parity_allTransaction` instead.")) } fn pending_transactions_stats(&self) -> Result> { @@ -332,11 +342,6 @@ impl Parity for ParityClient where } fn local_transactions(&self) -> Result> { - // Return nothing if accounts are disabled (running as public node) - if self.accounts.is_none() { - return Ok(BTreeMap::new()); - } - let transactions = self.miner.local_transactions(); let block_number = self.client.chain_info().best_block_number; Ok(transactions @@ -359,20 +364,11 @@ impl Parity for ParityClient where fn next_nonce(&self, address: H160) -> BoxFuture { let address: Address = address.into(); - Box::new(future::ok(self.miner.last_nonce(&address) - .map(|n| n + 1.into()) - .unwrap_or_else(|| self.client.latest_nonce(&address)) - .into() - )) + Box::new(future::ok(self.miner.next_nonce(&*self.client, &address).into())) } fn mode(&self) -> Result { - Ok(match self.client.mode() { - Mode::Off => "offline", - Mode::Dark(..) => "dark", - Mode::Passive(..) => "passive", - Mode::Active => "active", - }.into()) + Ok(self.client.mode().to_string()) } fn enode(&self) -> Result { @@ -405,13 +401,8 @@ impl Parity for ParityClient where fn node_kind(&self) -> Result<::v1::types::NodeKind> { use ::v1::types::{NodeKind, Availability, Capability}; - let availability = match self.accounts { - Some(_) => Availability::Personal, - None => Availability::Public - }; - Ok(NodeKind { - availability: availability, + availability: Availability::Personal, capability: Capability::Full, }) } @@ -475,9 +466,9 @@ impl Parity for ParityClient where }; let state = self.client.state_at(id).ok_or(errors::state_pruned())?; - let header = self.client.block_header(id).ok_or(errors::state_pruned())?; + let header = self.client.block_header(id).ok_or(errors::state_pruned())?.decode().map_err(errors::decode)?; - (state, header.decode()) + (state, header) }; self.client.call_many(&requests, &mut state, &header) diff --git a/rpc/src/v1/impls/parity_accounts.rs b/rpc/src/v1/impls/parity_accounts.rs index 8de8fea900f6a8aace81cdba0a15a403f72c070c..d7e1fd254696cb34bdac8779de0442e117e09fde 100644 --- a/rpc/src/v1/impls/parity_accounts.rs +++ b/rpc/src/v1/impls/parity_accounts.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -25,35 +25,27 @@ use ethcore::account_provider::AccountProvider; use jsonrpc_core::Result; use v1::helpers::errors; -use v1::helpers::accounts::unwrap_provider; use v1::traits::ParityAccounts; use v1::types::{H160 as RpcH160, H256 as RpcH256, H520 as RpcH520, DappId, Derive, DeriveHierarchical, DeriveHash, ExtAccountInfo}; /// Account management (personal) rpc implementation. pub struct ParityAccountsClient { - accounts: Option>, + accounts: Arc, } impl ParityAccountsClient { /// Creates new PersonalClient - pub fn new(store: &Option>) -> Self { + pub fn new(store: &Arc) -> Self { ParityAccountsClient { accounts: store.clone(), } } - - /// Attempt to get the `Arc`, errors if provider was not - /// set. - fn account_provider(&self) -> Result> { - unwrap_provider(&self.accounts) - } } impl ParityAccounts for ParityAccountsClient { fn all_accounts_info(&self) -> Result> { - let store = self.account_provider()?; - let info = store.accounts_info().map_err(|e| errors::account("Could not fetch account info.", e))?; - let other = store.addresses_info(); + let info = self.accounts.accounts_info().map_err(|e| errors::account("Could not fetch account info.", e))?; + let other = self.accounts.addresses_info(); let account_iter = info .into_iter() @@ -83,29 +75,23 @@ impl ParityAccounts for ParityAccountsClient { } fn new_account_from_phrase(&self, phrase: String, pass: String) -> Result { - let store = self.account_provider()?; - let brain = Brain::new(phrase).generate().unwrap(); - store.insert_account(brain.secret().clone(), &pass) + self.accounts.insert_account(brain.secret().clone(), &pass) .map(Into::into) .map_err(|e| errors::account("Could not create account.", e)) } fn new_account_from_wallet(&self, json: String, pass: String) -> Result { - let store = self.account_provider()?; - - store.import_presale(json.as_bytes(), &pass) - .or_else(|_| store.import_wallet(json.as_bytes(), &pass, true)) + self.accounts.import_presale(json.as_bytes(), &pass) + .or_else(|_| self.accounts.import_wallet(json.as_bytes(), &pass, true)) .map(Into::into) .map_err(|e| errors::account("Could not create account.", e)) } fn new_account_from_secret(&self, secret: RpcH256, pass: String) -> Result { - let store = self.account_provider()?; - let secret = Secret::from_unsafe_slice(&secret.0) .map_err(|e| errors::account("Could not create account.", e))?; - store.insert_account(secret, &pass) + self.accounts.insert_account(secret, &pass) .map(Into::into) .map_err(|e| errors::account("Could not create account.", e)) } @@ -113,14 +99,14 @@ impl ParityAccounts for ParityAccountsClient { fn test_password(&self, account: RpcH160, password: String) -> Result { let account: Address = account.into(); - self.account_provider()? + self.accounts .test_password(&account, &password) .map_err(|e| errors::account("Could not fetch account info.", e)) } fn change_password(&self, account: RpcH160, password: String, new_password: String) -> Result { let account: Address = account.into(); - self.account_provider()? + self.accounts .change_password(&account, password, new_password) .map(|_| true) .map_err(|e| errors::account("Could not fetch account info.", e)) @@ -128,181 +114,156 @@ impl ParityAccounts for ParityAccountsClient { fn kill_account(&self, account: RpcH160, password: String) -> Result { let account: Address = account.into(); - self.account_provider()? + self.accounts .kill_account(&account, &password) .map(|_| true) .map_err(|e| errors::account("Could not delete account.", e)) } fn remove_address(&self, addr: RpcH160) -> Result { - let store = self.account_provider()?; let addr: Address = addr.into(); - store.remove_address(addr); + self.accounts.remove_address(addr); Ok(true) } fn set_account_name(&self, addr: RpcH160, name: String) -> Result { - let store = self.account_provider()?; let addr: Address = addr.into(); - store.set_account_name(addr.clone(), name.clone()) - .unwrap_or_else(|_| store.set_address_name(addr, name)); + self.accounts.set_account_name(addr.clone(), name.clone()) + .unwrap_or_else(|_| self.accounts.set_address_name(addr, name)); Ok(true) } fn set_account_meta(&self, addr: RpcH160, meta: String) -> Result { - let store = self.account_provider()?; let addr: Address = addr.into(); - store.set_account_meta(addr.clone(), meta.clone()) - .unwrap_or_else(|_| store.set_address_meta(addr, meta)); + self.accounts.set_account_meta(addr.clone(), meta.clone()) + .unwrap_or_else(|_| self.accounts.set_address_meta(addr, meta)); Ok(true) } fn set_dapp_addresses(&self, dapp: DappId, addresses: Option>) -> Result { - let store = self.account_provider()?; - - store.set_dapp_addresses(dapp.into(), addresses.map(into_vec)) + self.accounts.set_dapp_addresses(dapp.into(), addresses.map(into_vec)) .map_err(|e| errors::account("Couldn't set dapp addresses.", e)) .map(|_| true) } fn dapp_addresses(&self, dapp: DappId) -> Result> { - let store = self.account_provider()?; - - store.dapp_addresses(dapp.into()) + self.accounts.dapp_addresses(dapp.into()) .map_err(|e| errors::account("Couldn't get dapp addresses.", e)) .map(into_vec) } fn set_dapp_default_address(&self, dapp: DappId, address: RpcH160) -> Result { - let store = self.account_provider()?; - - store.set_dapp_default_address(dapp.into(), address.into()) + self.accounts.set_dapp_default_address(dapp.into(), address.into()) .map_err(|e| errors::account("Couldn't set dapp default address.", e)) .map(|_| true) } fn dapp_default_address(&self, dapp: DappId) -> Result { - let store = self.account_provider()?; - - store.dapp_default_address(dapp.into()) + self.accounts.dapp_default_address(dapp.into()) .map_err(|e| errors::account("Couldn't get dapp default address.", e)) .map(Into::into) } fn set_new_dapps_addresses(&self, addresses: Option>) -> Result { - let store = self.account_provider()?; - - store + self.accounts .set_new_dapps_addresses(addresses.map(into_vec)) .map_err(|e| errors::account("Couldn't set dapps addresses.", e)) .map(|_| true) } fn new_dapps_addresses(&self) -> Result>> { - let store = self.account_provider()?; - - store.new_dapps_addresses() + self.accounts.new_dapps_addresses() .map_err(|e| errors::account("Couldn't get dapps addresses.", e)) .map(|accounts| accounts.map(into_vec)) } fn set_new_dapps_default_address(&self, address: RpcH160) -> Result { - let store = self.account_provider()?; - - store.set_new_dapps_default_address(address.into()) + self.accounts.set_new_dapps_default_address(address.into()) .map_err(|e| errors::account("Couldn't set new dapps default address.", e)) .map(|_| true) } fn new_dapps_default_address(&self) -> Result { - let store = self.account_provider()?; - - store.new_dapps_default_address() + self.accounts.new_dapps_default_address() .map_err(|e| errors::account("Couldn't get new dapps default address.", e)) .map(Into::into) } fn recent_dapps(&self) -> Result> { - let store = self.account_provider()?; - - store.recent_dapps() + self.accounts.recent_dapps() .map_err(|e| errors::account("Couldn't get recent dapps.", e)) .map(|map| map.into_iter().map(|(k, v)| (k.into(), v)).collect()) } fn import_geth_accounts(&self, addresses: Vec) -> Result> { - let store = self.account_provider()?; - - store + self.accounts .import_geth_accounts(into_vec(addresses), false) .map(into_vec) .map_err(|e| errors::account("Couldn't import Geth accounts", e)) } fn geth_accounts(&self) -> Result> { - let store = self.account_provider()?; - - Ok(into_vec(store.list_geth_accounts(false))) + Ok(into_vec(self.accounts.list_geth_accounts(false))) } fn create_vault(&self, name: String, password: String) -> Result { - self.account_provider()? + self.accounts .create_vault(&name, &password) .map_err(|e| errors::account("Could not create vault.", e)) .map(|_| true) } fn open_vault(&self, name: String, password: String) -> Result { - self.account_provider()? + self.accounts .open_vault(&name, &password) .map_err(|e| errors::account("Could not open vault.", e)) .map(|_| true) } fn close_vault(&self, name: String) -> Result { - self.account_provider()? + self.accounts .close_vault(&name) .map_err(|e| errors::account("Could not close vault.", e)) .map(|_| true) } fn list_vaults(&self) -> Result> { - self.account_provider()? + self.accounts .list_vaults() .map_err(|e| errors::account("Could not list vaults.", e)) } fn list_opened_vaults(&self) -> Result> { - self.account_provider()? + self.accounts .list_opened_vaults() .map_err(|e| errors::account("Could not list vaults.", e)) } fn change_vault_password(&self, name: String, new_password: String) -> Result { - self.account_provider()? + self.accounts .change_vault_password(&name, &new_password) .map_err(|e| errors::account("Could not change vault password.", e)) .map(|_| true) } fn change_vault(&self, address: RpcH160, new_vault: String) -> Result { - self.account_provider()? + self.accounts .change_vault(address.into(), &new_vault) .map_err(|e| errors::account("Could not change vault.", e)) .map(|_| true) } fn get_vault_meta(&self, name: String) -> Result { - self.account_provider()? + self.accounts .get_vault_meta(&name) .map_err(|e| errors::account("Could not get vault metadata.", e)) } fn set_vault_meta(&self, name: String, meta: String) -> Result { - self.account_provider()? + self.accounts .set_vault_meta(&name, &meta) .map_err(|e| errors::account("Could not update vault metadata.", e)) .map(|_| true) @@ -310,7 +271,7 @@ impl ParityAccounts for ParityAccountsClient { fn derive_key_index(&self, addr: RpcH160, password: String, derivation: DeriveHierarchical, save_as_account: bool) -> Result { let addr: Address = addr.into(); - self.account_provider()? + self.accounts .derive_account( &addr, Some(password), @@ -323,7 +284,7 @@ impl ParityAccounts for ParityAccountsClient { fn derive_key_hash(&self, addr: RpcH160, password: String, derivation: DeriveHash, save_as_account: bool) -> Result { let addr: Address = addr.into(); - self.account_provider()? + self.accounts .derive_account( &addr, Some(password), @@ -336,7 +297,7 @@ impl ParityAccounts for ParityAccountsClient { fn export_account(&self, addr: RpcH160, password: String) -> Result { let addr = addr.into(); - self.account_provider()? + self.accounts .export_account( &addr, password, @@ -346,7 +307,7 @@ impl ParityAccounts for ParityAccountsClient { } fn sign_message(&self, addr: RpcH160, password: String, message: RpcH256) -> Result { - self.account_provider()? + self.accounts .sign( addr.into(), Some(password), @@ -357,8 +318,7 @@ impl ParityAccounts for ParityAccountsClient { } fn hardware_pin_matrix_ack(&self, path: String, pin: String) -> Result { - let store = self.account_provider()?; - Ok(store.hardware_pin_matrix_ack(&path, &pin).map_err(|e| errors::account("Error communicating with hardware wallet.", e))?) + self.accounts.hardware_pin_matrix_ack(&path, &pin).map_err(|e| errors::account("Error communicating with hardware wallet.", e)) } } diff --git a/rpc/src/v1/impls/parity_set.rs b/rpc/src/v1/impls/parity_set.rs index 9d07118723005f56f4a1787d4614b5ed7f41b8bd..18f5776c693d124284665a078fbbe1e6b227291a 100644 --- a/rpc/src/v1/impls/parity_set.rs +++ b/rpc/src/v1/impls/parity_set.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -17,11 +17,11 @@ /// Parity-specific rpc interface for operations altering the settings. use std::io; use std::sync::Arc; +use std::time::Duration; +use ethcore::client::{BlockChainClient, Mode}; use ethcore::miner::MinerService; -use ethcore::client::MiningBlockChainClient; -use ethcore::mode::Mode; -use ethsync::ManageNetwork; +use sync::ManageNetwork; use fetch::{self, Fetch}; use futures_cpupool::CpuPool; use hash::keccak_buffer; @@ -47,7 +47,7 @@ pub struct ParitySetClient { } impl ParitySetClient - where C: MiningBlockChainClient + 'static, + where C: BlockChainClient + 'static, { /// Creates new `ParitySetClient` with given `Fetch`. pub fn new( @@ -73,24 +73,38 @@ impl ParitySetClient } impl ParitySet for ParitySetClient where - C: MiningBlockChainClient + 'static, + C: BlockChainClient + 'static, M: MinerService + 'static, U: UpdateService + 'static, F: Fetch + 'static, { - fn set_min_gas_price(&self, gas_price: U256) -> Result { - self.miner.set_minimal_gas_price(gas_price.into()); - Ok(true) + fn set_min_gas_price(&self, _gas_price: U256) -> Result { + warn!("setMinGasPrice is deprecated. Ignoring request."); + Ok(false) + } + + fn set_transactions_limit(&self, _limit: usize) -> Result { + warn!("setTransactionsLimit is deprecated. Ignoring request."); + Ok(false) + } + + fn set_tx_gas_limit(&self, _limit: U256) -> Result { + warn!("setTxGasLimit is deprecated. Ignoring request."); + Ok(false) } fn set_gas_floor_target(&self, target: U256) -> Result { - self.miner.set_gas_floor_target(target.into()); + let mut range = self.miner.authoring_params().gas_range_target.clone(); + range.0 = target.into(); + self.miner.set_gas_range_target(range); Ok(true) } fn set_gas_ceil_target(&self, target: U256) -> Result { - self.miner.set_gas_ceil_target(target.into()); + let mut range = self.miner.authoring_params().gas_range_target.clone(); + range.1 = target.into(); + self.miner.set_gas_range_target(range); Ok(true) } @@ -99,23 +113,13 @@ impl ParitySet for ParitySetClient where Ok(true) } - fn set_author(&self, author: H160) -> Result { - self.miner.set_author(author.into()); + fn set_author(&self, address: H160) -> Result { + self.miner.set_author(address.into(), None).map_err(Into::into).map_err(errors::password)?; Ok(true) } fn set_engine_signer(&self, address: H160, password: String) -> Result { - self.miner.set_engine_signer(address.into(), password).map_err(Into::into).map_err(errors::password)?; - Ok(true) - } - - fn set_transactions_limit(&self, limit: usize) -> Result { - self.miner.set_transactions_limit(limit); - Ok(true) - } - - fn set_tx_gas_limit(&self, limit: U256) -> Result { - self.miner.set_tx_gas_limit(limit.into()); + self.miner.set_author(address.into(), Some(password)).map_err(Into::into).map_err(errors::password)?; Ok(true) } @@ -156,8 +160,8 @@ impl ParitySet for ParitySetClient where fn set_mode(&self, mode: String) -> Result { self.client.set_mode(match mode.as_str() { "offline" => Mode::Off, - "dark" => Mode::Dark(300), - "passive" => Mode::Passive(300, 3600), + "dark" => Mode::Dark(Duration::from_secs(300)), + "passive" => Mode::Passive(Duration::from_secs(300), Duration::from_secs(3600)), "active" => Mode::Active, e => { return Err(errors::invalid_params("mode", e.to_owned())); }, }); @@ -170,7 +174,7 @@ impl ParitySet for ParitySetClient where } fn hash_content(&self, url: String) -> BoxFuture { - let future = self.fetch.fetch(&url, Default::default()).then(move |result| { + let future = self.fetch.get(&url, Default::default()).then(move |result| { result .map_err(errors::fetch) .and_then(move |response| { @@ -202,6 +206,8 @@ impl ParitySet for ParitySetClient where let block_number = self.client.chain_info().best_block_number; let hash = hash.into(); - Ok(self.miner.remove_pending_transaction(&*self.client, &hash).map(|t| Transaction::from_pending(t, block_number, self.eip86_transition))) + Ok(self.miner.remove_transaction(&hash) + .map(|t| Transaction::from_pending(t.pending().clone(), block_number + 1, self.eip86_transition)) + ) } } diff --git a/rpc/src/v1/impls/personal.rs b/rpc/src/v1/impls/personal.rs index c3560fe9df395d5873e0aa0c6674751cd0c3aac8..3a8d13c82e96a18352ccebf8a52d8d0960085043 100644 --- a/rpc/src/v1/impls/personal.rs +++ b/rpc/src/v1/impls/personal.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -16,6 +16,7 @@ //! Account management (personal) rpc implementation use std::sync::Arc; +use std::time::Duration; use bytes::{Bytes, ToPretty}; use ethcore::account_provider::AccountProvider; @@ -27,7 +28,6 @@ use jsonrpc_core::{BoxFuture, Result}; use jsonrpc_core::futures::{future, Future}; use v1::helpers::errors; use v1::helpers::dispatch::{self, eth_data_hash, Dispatcher, SignWith}; -use v1::helpers::accounts::unwrap_provider; use v1::traits::Personal; use v1::types::{ H160 as RpcH160, H256 as RpcH256, H520 as RpcH520, U128 as RpcU128, @@ -41,30 +41,26 @@ use v1::metadata::Metadata; /// Account management (personal) rpc implementation. pub struct PersonalClient { - accounts: Option>, + accounts: Arc, dispatcher: D, allow_perm_unlock: bool, } impl PersonalClient { /// Creates new PersonalClient - pub fn new(accounts: Option>, dispatcher: D, allow_perm_unlock: bool) -> Self { + pub fn new(accounts: &Arc, dispatcher: D, allow_perm_unlock: bool) -> Self { PersonalClient { - accounts, + accounts: accounts.clone(), dispatcher, allow_perm_unlock, } } - - fn account_provider(&self) -> Result> { - unwrap_provider(&self.accounts) - } } impl PersonalClient { fn do_sign_transaction(&self, meta: Metadata, request: TransactionRequest, password: String) -> BoxFuture<(PendingTransaction, D)> { let dispatcher = self.dispatcher.clone(); - let accounts = try_bf!(self.account_provider()); + let accounts = self.accounts.clone(); let default = match request.from.as_ref() { Some(account) => Ok(account.clone().into()), @@ -94,22 +90,19 @@ impl Personal for PersonalClient { type Metadata = Metadata; fn accounts(&self) -> Result> { - let store = self.account_provider()?; - let accounts = store.accounts().map_err(|e| errors::account("Could not fetch accounts.", e))?; + let accounts = self.accounts.accounts().map_err(|e| errors::account("Could not fetch accounts.", e))?; Ok(accounts.into_iter().map(Into::into).collect::>()) } fn new_account(&self, pass: String) -> Result { - let store = self.account_provider()?; - - store.new_account(&pass) + self.accounts.new_account(&pass) .map(Into::into) .map_err(|e| errors::account("Could not create account.", e)) } fn unlock_account(&self, account: RpcH160, account_pass: String, duration: Option) -> Result { let account: Address = account.into(); - let store = self.account_provider()?; + let store = self.accounts.clone(); let duration = match duration { None => None, Some(duration) => { @@ -130,8 +123,8 @@ impl Personal for PersonalClient { Some("Restart your client with --geth flag or use personal_sendTransaction instead."), )), (true, Some(0)) => store.unlock_account_permanently(account, account_pass), - (true, Some(d)) => store.unlock_account_timed(account, account_pass, d * 1000), - (true, None) => store.unlock_account_timed(account, account_pass, 300_000), + (true, Some(d)) => store.unlock_account_timed(account, account_pass, Duration::from_secs(d.into())), + (true, None) => store.unlock_account_timed(account, account_pass, Duration::from_secs(300)), }; match r { Ok(_) => Ok(true), @@ -141,7 +134,7 @@ impl Personal for PersonalClient { fn sign(&self, data: RpcBytes, account: RpcH160, password: String) -> BoxFuture { let dispatcher = self.dispatcher.clone(); - let accounts = try_bf!(self.account_provider()); + let accounts = self.accounts.clone(); let payload = RpcConfirmationPayload::EthSignMessage((account.clone(), data).into()); diff --git a/rpc/src/v1/impls/private.rs b/rpc/src/v1/impls/private.rs new file mode 100644 index 0000000000000000000000000000000000000000..a1110eed1131e5387704b8b8c253dacce232101f --- /dev/null +++ b/rpc/src/v1/impls/private.rs @@ -0,0 +1,121 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Privte transaction signing RPC implementation. + +use std::sync::Arc; + +use rlp::Rlp; + +use ethcore_private_tx::Provider as PrivateTransactionManager; +use ethereum_types::Address; +use transaction::SignedTransaction; + +use jsonrpc_core::{Error}; +use v1::types::{Bytes, PrivateTransactionReceipt, H160, H256, TransactionRequest, U256, + BlockNumber, PrivateTransactionReceiptAndTransaction, CallRequest, block_number_to_id}; +use v1::traits::Private; +use v1::metadata::Metadata; +use v1::helpers::{errors, fake_sign}; + +/// Private transaction manager API endpoint implementation. +pub struct PrivateClient { + private: Option>, +} + +impl PrivateClient { + /// Creates a new instance. + pub fn new(private: Option>) -> Self { + PrivateClient { + private, + } + } + + fn unwrap_manager(&self) -> Result<&PrivateTransactionManager, Error> { + match self.private { + Some(ref arc) => Ok(&**arc), + None => Err(errors::light_unimplemented(None)), + } + } +} + +impl Private for PrivateClient { + type Metadata = Metadata; + + fn send_transaction(&self, request: Bytes) -> Result { + let signed_transaction = Rlp::new(&request.into_vec()).as_val() + .map_err(errors::rlp) + .and_then(|tx| SignedTransaction::new(tx).map_err(errors::transaction))?; + let client = self.unwrap_manager()?; + let receipt = client.create_private_transaction(signed_transaction).map_err(|e| errors::private_message(e))?; + Ok(receipt.into()) + } + + fn compose_deployment_transaction(&self, block_number: BlockNumber, request: Bytes, validators: Vec, gas_price: U256) -> Result { + let signed_transaction = Rlp::new(&request.into_vec()).as_val() + .map_err(errors::rlp) + .and_then(|tx| SignedTransaction::new(tx).map_err(errors::transaction))?; + let client = self.unwrap_manager()?; + + let addresses: Vec
= validators.into_iter().map(Into::into).collect(); + let id = match block_number { + BlockNumber::Pending => return Err(errors::private_message_block_id_not_supported()), + num => block_number_to_id(num) + }; + + let (transaction, contract_address) = client.public_creation_transaction(id, &signed_transaction, addresses.as_slice(), gas_price.into()) + .map_err(|e| errors::private_message(e))?; + let tx_hash = transaction.hash(None); + let request = TransactionRequest { + from: Some(signed_transaction.sender().into()), + to: None, + nonce: Some(transaction.nonce.into()), + gas_price: Some(transaction.gas_price.into()), + gas: Some(transaction.gas.into()), + value: Some(transaction.value.into()), + data: Some(transaction.data.into()), + condition: None, + }; + + Ok(PrivateTransactionReceiptAndTransaction { + transaction: request, + receipt: PrivateTransactionReceipt { + transaction_hash: tx_hash.into(), + contract_address: contract_address.map(|address| address.into()), + status_code: 0, + } + }) + } + + fn private_call(&self, meta: Self::Metadata, block_number: BlockNumber, request: CallRequest) -> Result { + let id = match block_number { + BlockNumber::Pending => return Err(errors::private_message_block_id_not_supported()), + num => block_number_to_id(num) + }; + + let request = CallRequest::into(request); + let signed = fake_sign::sign_call(request, meta.is_dapp())?; + let client = self.unwrap_manager()?; + let executed_result = client.private_call(id, &signed).map_err(|e| errors::private_message(e))?; + Ok(executed_result.output.into()) + } + + fn private_contract_key(&self, contract_address: H160) -> Result { + let client = self.unwrap_manager()?; + let key = client.contract_key_id(&contract_address.into()).map_err(|e| errors::private_message(e))?; + Ok(key.into()) + } +} diff --git a/rpc/src/v1/impls/pubsub.rs b/rpc/src/v1/impls/pubsub.rs index 59eef19533fb005d8d10ce2c4f9d4db5456f3f80..564c8b90d5ea9e539290137269e79cc5ac8559de 100644 --- a/rpc/src/v1/impls/pubsub.rs +++ b/rpc/src/v1/impls/pubsub.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/impls/rpc.rs b/rpc/src/v1/impls/rpc.rs index 3c76a316464a42677abfdf9c5772a6957c3b321f..9f15cc1a384bf39aa1ed5c7e954d287b9929cc16 100644 --- a/rpc/src/v1/impls/rpc.rs +++ b/rpc/src/v1/impls/rpc.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/impls/secretstore.rs b/rpc/src/v1/impls/secretstore.rs index 5a60192e8aff57aee1ffd021fe7f08d20fc94d7a..771599eca3b948b797121eb5ef19b6d382325d41 100644 --- a/rpc/src/v1/impls/secretstore.rs +++ b/rpc/src/v1/impls/secretstore.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -25,7 +25,6 @@ use ethcore::account_provider::AccountProvider; use jsonrpc_core::Result; use v1::helpers::errors; -use v1::helpers::accounts::unwrap_provider; use v1::helpers::secretstore::{generate_document_key, encrypt_document, decrypt_document, decrypt_document_with_shadow, ordered_servers_keccak}; use v1::traits::SecretStore; @@ -33,27 +32,20 @@ use v1::types::{H160, H256, H512, Bytes, EncryptedDocumentKey}; /// Parity implementation. pub struct SecretStoreClient { - accounts: Option>, + accounts: Arc, } impl SecretStoreClient { /// Creates new SecretStoreClient - pub fn new(store: &Option>) -> Self { + pub fn new(store: &Arc) -> Self { SecretStoreClient { accounts: store.clone(), } } - /// Attempt to get the `Arc`, errors if provider was not - /// set. - fn account_provider(&self) -> Result> { - unwrap_provider(&self.accounts) - } - /// Decrypt public key using account' private key fn decrypt_key(&self, address: H160, password: String, key: Bytes) -> Result> { - let store = self.account_provider()?; - store.decrypt(address.into(), Some(password), &DEFAULT_MAC, &key.0) + self.accounts.decrypt(address.into(), Some(password), &DEFAULT_MAC, &key.0) .map_err(|e| errors::account("Could not decrypt key.", e)) } @@ -66,8 +58,7 @@ impl SecretStoreClient { impl SecretStore for SecretStoreClient { fn generate_document_key(&self, address: H160, password: String, server_key_public: H512) -> Result { - let store = self.account_provider()?; - let account_public = store.account_public(address.into(), &password) + let account_public = self.accounts.account_public(address.into(), &password) .map_err(|e| errors::account("Could not read account public.", e))?; generate_document_key(account_public, server_key_public.into()) } @@ -97,8 +88,7 @@ impl SecretStore for SecretStoreClient { } fn sign_raw_hash(&self, address: H160, password: String, raw_hash: H256) -> Result { - let store = self.account_provider()?; - store + self.accounts .sign(address.into(), Some(password), raw_hash.into()) .map(|s| Bytes::new((*s).to_vec())) .map_err(|e| errors::account("Could not sign raw hash.", e)) diff --git a/rpc/src/v1/impls/signer.rs b/rpc/src/v1/impls/signer.rs index ce3f13b976742bea163a34e23807349be1493135..58c48bb32f5a6422c7e07bbb73c64c4d18a746c5 100644 --- a/rpc/src/v1/impls/signer.rs +++ b/rpc/src/v1/impls/signer.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -22,7 +22,7 @@ use ethcore::account_provider::AccountProvider; use ethkey; use parity_reactor::Remote; use parking_lot::Mutex; -use rlp::UntrustedRlp; +use rlp::Rlp; use transaction::{SignedTransaction, PendingTransaction}; use jsonrpc_core::{Result, BoxFuture, Error}; @@ -30,7 +30,6 @@ use jsonrpc_core::futures::{future, Future, IntoFuture}; use jsonrpc_core::futures::future::Either; use jsonrpc_pubsub::SubscriptionId; use jsonrpc_macros::pubsub::{Sink, Subscriber}; -use v1::helpers::accounts::unwrap_provider; use v1::helpers::dispatch::{self, Dispatcher, WithToken, eth_data_hash}; use v1::helpers::{errors, SignerService, SigningQueue, ConfirmationPayload, FilledTransactionRequest, Subscribers}; use v1::metadata::Metadata; @@ -40,7 +39,7 @@ use v1::types::{TransactionModification, ConfirmationRequest, ConfirmationRespon /// Transactions confirmation (personal) rpc implementation. pub struct SignerClient { signer: Arc, - accounts: Option>, + accounts: Arc, dispatcher: D, subscribers: Arc>>>>, } @@ -48,7 +47,7 @@ pub struct SignerClient { impl SignerClient { /// Create new instance of signer client. pub fn new( - store: &Option>, + store: &Arc, dispatcher: D, signer: &Arc, remote: Remote, @@ -78,25 +77,20 @@ impl SignerClient { } } - fn account_provider(&self) -> Result> { - unwrap_provider(&self.accounts) - } - fn confirm_internal(&self, id: U256, modification: TransactionModification, f: F) -> BoxFuture> where F: FnOnce(D, Arc, ConfirmationPayload) -> T, T: IntoFuture, Error=Error>, T::Future: Send + 'static { let id = id.into(); - let accounts = try_bf!(self.account_provider()); let dispatcher = self.dispatcher.clone(); let signer = self.signer.clone(); - Box::new(signer.peek(&id).map(|confirmation| { - let mut payload = confirmation.payload.clone(); + Box::new(signer.take(&id).map(|sender| { + let mut payload = sender.request.payload.clone(); // Modify payload if let ConfirmationPayload::SendTransaction(ref mut request) = payload { - if let Some(sender) = modification.sender.clone() { + if let Some(sender) = modification.sender { request.from = sender.into(); // Altering sender should always reset the nonce. request.nonce = None; @@ -111,11 +105,13 @@ impl SignerClient { request.condition = condition.clone().map(Into::into); } } - let fut = f(dispatcher, accounts, payload); + let fut = f(dispatcher, self.accounts.clone(), payload); Either::A(fut.into_future().then(move |result| { // Execute if let Ok(ref response) = result { - signer.request_confirmed(id, Ok((*response).clone())); + signer.request_confirmed(sender, Ok((*response).clone())); + } else { + signer.request_untouched(sender); } result @@ -127,7 +123,7 @@ impl SignerClient { fn verify_transaction(bytes: Bytes, request: FilledTransactionRequest, process: F) -> Result where F: FnOnce(PendingTransaction) -> Result, { - let signed_transaction = UntrustedRlp::new(&bytes.0).as_val().map_err(errors::rlp)?; + let signed_transaction = Rlp::new(&bytes.0).as_val().map_err(errors::rlp)?; let signed_transaction = SignedTransaction::new(signed_transaction).map_err(|e| errors::invalid_params("Invalid signature.", e))?; let sender = signed_transaction.sender(); @@ -194,8 +190,9 @@ impl Signer for SignerClient { fn confirm_request_raw(&self, id: U256, bytes: Bytes) -> Result { let id = id.into(); - self.signer.peek(&id).map(|confirmation| { - let result = match confirmation.payload { + self.signer.take(&id).map(|sender| { + let payload = sender.request.payload.clone(); + let result = match payload { ConfirmationPayload::SendTransaction(request) => { Self::verify_transaction(bytes, request, |pending_transaction| { self.dispatcher.dispatch_transaction(pending_transaction) @@ -224,14 +221,16 @@ impl Signer for SignerClient { }, }; if let Ok(ref response) = result { - self.signer.request_confirmed(id, Ok(response.clone())); + self.signer.request_confirmed(sender, Ok(response.clone())); + } else { + self.signer.request_untouched(sender); } result }).unwrap_or_else(|| Err(errors::invalid_params("Unknown RequestID", id))) } fn reject_request(&self, id: U256) -> Result { - let res = self.signer.request_rejected(id.into()); + let res = self.signer.take(&id.into()).map(|sender| self.signer.request_rejected(sender)); Ok(res.is_some()) } diff --git a/rpc/src/v1/impls/signing.rs b/rpc/src/v1/impls/signing.rs index d8a27e95265a7d67f2e55942ce71e8184c5a0e41..b22bbc80dd882fb6cc3f6437fe39fda50cc4ca82 100644 --- a/rpc/src/v1/impls/signing.rs +++ b/rpc/src/v1/impls/signing.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -32,7 +32,6 @@ use v1::helpers::{ ConfirmationResult as RpcConfirmationResult, }; use v1::helpers::dispatch::{self, Dispatcher}; -use v1::helpers::accounts::unwrap_provider; use v1::metadata::Metadata; use v1::traits::{EthSigning, ParitySigning}; use v1::types::{ @@ -90,7 +89,7 @@ fn schedule(remote: Remote, /// Implementation of functions that require signing when no trusted signer is used. pub struct SigningQueueClient { signer: Arc, - accounts: Option>, + accounts: Arc, dispatcher: D, remote: Remote, // None here means that the request hasn't yet been confirmed @@ -99,7 +98,7 @@ pub struct SigningQueueClient { impl SigningQueueClient { /// Creates a new signing queue client given shared signing queue. - pub fn new(signer: &Arc, dispatcher: D, remote: Remote, accounts: &Option>) -> Self { + pub fn new(signer: &Arc, dispatcher: D, remote: Remote, accounts: &Arc) -> Self { SigningQueueClient { signer: signer.clone(), accounts: accounts.clone(), @@ -109,12 +108,8 @@ impl SigningQueueClient { } } - fn account_provider(&self) -> Result> { - unwrap_provider(&self.accounts) - } - fn dispatch(&self, payload: RpcConfirmationPayload, default_account: DefaultAccount, origin: Origin) -> BoxFuture { - let accounts = try_bf!(self.account_provider()); + let accounts = self.accounts.clone(); let default_account = match default_account { DefaultAccount::Provided(acc) => acc, DefaultAccount::ForDapp(dapp) => accounts.dapp_default_address(dapp).ok().unwrap_or_default(), @@ -144,8 +139,7 @@ impl ParitySigning for SigningQueueClient { type Metadata = Metadata; fn compose_transaction(&self, meta: Metadata, transaction: RpcTransactionRequest) -> BoxFuture { - let accounts = try_bf!(self.account_provider()); - let default_account = accounts.dapp_default_address(meta.dapp_id().into()).ok().unwrap_or_default(); + let default_account = self.accounts.dapp_default_address(meta.dapp_id().into()).ok().unwrap_or_default(); Box::new(self.dispatcher.fill_optional_fields(transaction.into(), default_account, true).map(Into::into)) } diff --git a/rpc/src/v1/impls/signing_unsafe.rs b/rpc/src/v1/impls/signing_unsafe.rs index ddac36a61e67208bce665699a53429987032e7bc..6016cbbfc0b65428c32b3087d927300c22c0bc08 100644 --- a/rpc/src/v1/impls/signing_unsafe.rs +++ b/rpc/src/v1/impls/signing_unsafe.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -24,7 +24,6 @@ use jsonrpc_core::{BoxFuture, Result}; use jsonrpc_core::futures::{future, Future}; use v1::helpers::{errors, DefaultAccount}; use v1::helpers::dispatch::{self, Dispatcher}; -use v1::helpers::accounts::unwrap_provider; use v1::metadata::Metadata; use v1::traits::{EthSigning, ParitySigning}; use v1::types::{ @@ -39,25 +38,21 @@ use v1::types::{ /// Implementation of functions that require signing when no trusted signer is used. pub struct SigningUnsafeClient { - accounts: Option>, + accounts: Arc, dispatcher: D, } impl SigningUnsafeClient { /// Creates new SigningUnsafeClient. - pub fn new(accounts: &Option>, dispatcher: D) -> Self { + pub fn new(accounts: &Arc, dispatcher: D) -> Self { SigningUnsafeClient { accounts: accounts.clone(), dispatcher: dispatcher, } } - fn account_provider(&self) -> Result> { - unwrap_provider(&self.accounts) - } - fn handle(&self, payload: RpcConfirmationPayload, account: DefaultAccount) -> BoxFuture { - let accounts = try_bf!(self.account_provider()); + let accounts = self.accounts.clone(); let default = match account { DefaultAccount::Provided(acc) => acc, DefaultAccount::ForDapp(dapp) => accounts.dapp_default_address(dapp).ok().unwrap_or_default(), @@ -108,7 +103,7 @@ impl ParitySigning for SigningUnsafeClient { type Metadata = Metadata; fn compose_transaction(&self, meta: Metadata, transaction: RpcTransactionRequest) -> BoxFuture { - let accounts = try_bf!(self.account_provider()); + let accounts = self.accounts.clone(); let default_account = accounts.dapp_default_address(meta.dapp_id().into()).ok().unwrap_or_default(); Box::new(self.dispatcher.fill_optional_fields(transaction.into(), default_account, true).map(Into::into)) } diff --git a/rpc/src/v1/impls/traces.rs b/rpc/src/v1/impls/traces.rs index 0ee1afd5fa7c5628b484bd434c53bb999cee4228..0e43d8c11af177d579657d255718f22ada376650 100644 --- a/rpc/src/v1/impls/traces.rs +++ b/rpc/src/v1/impls/traces.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -18,8 +18,8 @@ use std::sync::Arc; -use ethcore::client::{MiningBlockChainClient, CallAnalytics, TransactionId, TraceId, StateClient, StateInfo, Call, BlockId}; -use rlp::UntrustedRlp; +use ethcore::client::{BlockChainClient, CallAnalytics, TransactionId, TraceId, StateClient, StateInfo, Call, BlockId}; +use rlp::Rlp; use transaction::SignedTransaction; use jsonrpc_core::Result; @@ -53,7 +53,7 @@ impl TracesClient { impl Traces for TracesClient where S: StateInfo + 'static, - C: MiningBlockChainClient + StateClient + Call + 'static + C: BlockChainClient + StateClient + Call + 'static { type Metadata = Metadata; @@ -104,7 +104,7 @@ impl Traces for TracesClient where let mut state = self.client.state_at(id).ok_or(errors::state_pruned())?; let header = self.client.block_header(id).ok_or(errors::state_pruned())?; - self.client.call(&signed, to_call_analytics(flags), &mut state, &header.decode()) + self.client.call(&signed, to_call_analytics(flags), &mut state, &header.decode().map_err(errors::decode)?) .map(TraceResults::from) .map_err(errors::call) } @@ -131,7 +131,7 @@ impl Traces for TracesClient where let mut state = self.client.state_at(id).ok_or(errors::state_pruned())?; let header = self.client.block_header(id).ok_or(errors::state_pruned())?; - self.client.call_many(&requests, &mut state, &header.decode()) + self.client.call_many(&requests, &mut state, &header.decode().map_err(errors::decode)?) .map(|results| results.into_iter().map(TraceResults::from).collect()) .map_err(errors::call) } @@ -139,7 +139,7 @@ impl Traces for TracesClient where fn raw_transaction(&self, raw_transaction: Bytes, flags: TraceOptions, block: Trailing) -> Result { let block = block.unwrap_or_default(); - let tx = UntrustedRlp::new(&raw_transaction.into_vec()).as_val().map_err(|e| errors::invalid_params("Transaction is not valid RLP", e))?; + let tx = Rlp::new(&raw_transaction.into_vec()).as_val().map_err(|e| errors::invalid_params("Transaction is not valid RLP", e))?; let signed = SignedTransaction::new(tx).map_err(errors::transaction)?; let id = match block { @@ -153,7 +153,7 @@ impl Traces for TracesClient where let mut state = self.client.state_at(id).ok_or(errors::state_pruned())?; let header = self.client.block_header(id).ok_or(errors::state_pruned())?; - self.client.call(&signed, to_call_analytics(flags), &mut state, &header.decode()) + self.client.call(&signed, to_call_analytics(flags), &mut state, &header.decode().map_err(errors::decode)?) .map(TraceResults::from) .map_err(errors::call) } diff --git a/rpc/src/v1/impls/web3.rs b/rpc/src/v1/impls/web3.rs index 6915af1d1e0a4be7c4b5434e5b21bff19b50931f..aa304472856f78a506ce34b50bb0b527d873e1f8 100644 --- a/rpc/src/v1/impls/web3.rs +++ b/rpc/src/v1/impls/web3.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -31,7 +31,7 @@ impl Web3Client { impl Web3 for Web3Client { fn client_version(&self) -> Result { - Ok(version().to_owned().replace("Parity/", "Parity//")) + Ok(version().to_owned().replacen("/", "//", 1)) } fn sha3(&self, data: Bytes) -> Result { diff --git a/rpc/src/v1/informant.rs b/rpc/src/v1/informant.rs index 9a9cde3837cdd57f5c5f47ca42867fba61224557..07a70eeb10ed7a46813b7ada58a0cd5fb4e9d926 100644 --- a/rpc/src/v1/informant.rs +++ b/rpc/src/v1/informant.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/metadata.rs b/rpc/src/v1/metadata.rs index f0644d455c02e429db746c390bd86196db018dca..970ec60e486dd4b2829106dec822b5c1bff7e2f0 100644 --- a/rpc/src/v1/metadata.rs +++ b/rpc/src/v1/metadata.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/mod.rs b/rpc/src/v1/mod.rs index b5e1cfa4dba46182e776aa9bd7be9d4733726bef..cb510ae2941d13db08e6815fd4170c90204434cf 100644 --- a/rpc/src/v1/mod.rs +++ b/rpc/src/v1/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -41,7 +41,7 @@ pub mod informant; pub mod metadata; pub mod traits; -pub use self::traits::{Web3, Eth, EthFilter, EthPubSub, EthSigning, Net, Parity, ParityAccounts, ParitySet, ParitySigning, PubSub, Signer, Personal, Traces, Rpc, SecretStore}; +pub use self::traits::{Web3, Eth, EthFilter, EthPubSub, EthSigning, Net, Parity, ParityAccounts, ParitySet, ParitySigning, PubSub, Signer, Personal, Traces, Rpc, SecretStore, Private}; pub use self::impls::*; pub use self::helpers::{NetworkSettings, block_import, dispatch}; pub use self::metadata::Metadata; diff --git a/rpc/src/v1/tests/eth.rs b/rpc/src/v1/tests/eth.rs index 84dd64644d754856513b2f84d4d22158324281db..7354eb18b0a4da6d622f68e97f19a393cdac2efa 100644 --- a/rpc/src/v1/tests/eth.rs +++ b/rpc/src/v1/tests/eth.rs @@ -1,4 +1,4 @@ -// Copyright 2016 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -17,15 +17,14 @@ //! rpc integration tests. use std::env; use std::sync::Arc; -use std::time::Duration; -use ethereum_types::{U256, H256, Address}; +use ethereum_types::{H256, Address}; use ethcore::account_provider::AccountProvider; use ethcore::block::Block; use ethcore::client::{BlockChainClient, Client, ClientConfig, ChainInfo, ImportBlock}; use ethcore::ethereum; use ethcore::ids::BlockId; -use ethcore::miner::{MinerOptions, Banning, GasPricer, Miner, PendingSet, GasLimit}; +use ethcore::miner::Miner; use ethcore::spec::{Genesis, Spec}; use ethcore::views::BlockView; use ethjson::blockchain::BlockChain; @@ -33,7 +32,6 @@ use ethjson::state::test::ForkSpec; use io::IoChannel; use kvdb_memorydb; use miner::external::ExternalMiner; -use miner::transaction_queue::PrioritizationStrategy; use parking_lot::Mutex; use jsonrpc_core::IoHandler; @@ -58,31 +56,7 @@ fn sync_provider() -> Arc { } fn miner_service(spec: &Spec, accounts: Arc) -> Arc { - Miner::new( - MinerOptions { - new_work_notify: vec![], - force_sealing: true, - reseal_on_external_tx: true, - reseal_on_own_tx: true, - reseal_on_uncle: false, - tx_queue_size: 1024, - tx_gas_limit: !U256::zero(), - tx_queue_strategy: PrioritizationStrategy::GasPriceOnly, - tx_queue_gas_limit: GasLimit::None, - tx_queue_banning: Banning::Disabled, - tx_queue_memory_limit: None, - pending_set: PendingSet::SealingOrElseQueue, - reseal_min_period: Duration::from_secs(0), - reseal_max_period: Duration::from_secs(120), - work_queue_size: 50, - enable_resubmission: true, - refuse_service_transactions: false, - infinite_pending_block: false, - }, - GasPricer::new_fixed(20_000_000_000u64.into()), - &spec, - Some(accounts), - ) + Arc::new(Miner::new_for_tests(spec, Some(accounts))) } fn snapshot_service() -> Arc { @@ -127,7 +101,7 @@ impl EthTester { fn from_spec(spec: Spec) -> Self { let account_provider = account_provider(); - let opt_account_provider = Some(account_provider.clone()); + let opt_account_provider = account_provider.clone(); let miner_service = miner_service(&spec, account_provider.clone()); let snapshot_service = snapshot_service(); @@ -230,6 +204,18 @@ fn eth_get_block() { assert_eq!(tester.handler.handle_request_sync(req_block).unwrap(), res_block); } +#[test] +fn eth_get_block_by_hash() { + let chain = extract_chain!("BlockchainTests/bcGasPricerTest/RPC_API_Test"); + let tester = EthTester::from_chain(&chain); + + // We're looking for block number 4 from "RPC_API_Test_Frontier" + let req_block = r#"{"method":"eth_getBlockByHash","params":["0x9c9bdab4cb53fd834e790b13545597f026494d42112e84c0aca9dd6bcc545295",false],"id":1,"jsonrpc":"2.0"}"#; + + let res_block = r#"{"jsonrpc":"2.0","result":{"author":"0x8888f1f195afa192cfee860698584c030f4c9db1","difficulty":"0x200c0","extraData":"0x","gasLimit":"0x1dd8112","gasUsed":"0x5458","hash":"0x9c9bdab4cb53fd834e790b13545597f026494d42112e84c0aca9dd6bcc545295","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x8888f1f195afa192cfee860698584c030f4c9db1","mixHash":"0xaddea8d25bb0f955fa6c1d58d74ab8a3fec99d37943e2a261e3b12f97d6bff7c","nonce":"0x8e18bed16d5a88da","number":"0x4","parentHash":"0x2cbf4fc930c5b4c87598f43fc8eb26dccdab2f58a7d0d3ca92ec60a5444a330e","receiptsRoot":"0x7ed8026cf72ed0e98e6fd53ab406e51ffd34397d9da0052494ff41376fda7b5f","sealFields":["0xa0addea8d25bb0f955fa6c1d58d74ab8a3fec99d37943e2a261e3b12f97d6bff7c","0x888e18bed16d5a88da"],"sha3Uncles":"0x75cc08a7cb2cf8081446659fecb2633fb6b922d26edd59bd2272b1f5cae1c78b","size":"0x661","stateRoot":"0x68805721294e365020aca15ed56c360d9dc2cf03cbeff84c9b84b8aed023bfb5","timestamp":"0x59d662ff","totalDifficulty":"0xa0180","transactions":["0xb094b9dc356dbb8b256402c6d5709288066ad6a372c90c9c516f14277545fd58"],"transactionsRoot":"0x97a593d8d7e15b57f5c6bb25bc6c325463ef99f874bc08a78656c3ab5cb23262","uncles":["0xa1e9c9ecd2af999e0723aae1dc55dd9789ca618e0b34badcc8ac7d9a3dad3af2","0x81d429b6b6635214a2b0f976cc4b2ed49808140d6bede50129bc10d22ac9249e"]},"id":1}"#; + assert_eq!(tester.handler.handle_request_sync(req_block).unwrap(), res_block); +} + // a frontier-like test with an expanded gas limit and balance on known account. const TRANSACTION_COUNT_SPEC: &'static [u8] = br#"{ "name": "Frontier (Test)", @@ -337,7 +323,7 @@ fn eth_transaction_count() { let req_before = r#"{ "jsonrpc": "2.0", "method": "eth_getTransactionCount", - "params": [""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"", "latest"], + "params": [""#.to_owned() + format!("0x{:x}", address).as_ref() + r#"", "latest"], "id": 15 }"#; @@ -349,7 +335,7 @@ fn eth_transaction_count() { "jsonrpc": "2.0", "method": "eth_sendTransaction", "params": [{ - "from": ""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"", + "from": ""#.to_owned() + format!("0x{:x}", address).as_ref() + r#"", "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", "gas": "0x30000", "gasPrice": "0x1", @@ -365,7 +351,7 @@ fn eth_transaction_count() { let req_after_latest = r#"{ "jsonrpc": "2.0", "method": "eth_getTransactionCount", - "params": [""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"", "latest"], + "params": [""#.to_owned() + format!("0x{:x}", address).as_ref() + r#"", "latest"], "id": 17 }"#; @@ -377,7 +363,7 @@ fn eth_transaction_count() { let req_after_pending = r#"{ "jsonrpc": "2.0", "method": "eth_getTransactionCount", - "params": [""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"", "pending"], + "params": [""#.to_owned() + format!("0x{:x}", address).as_ref() + r#"", "pending"], "id": 18 }"#; @@ -403,7 +389,7 @@ fn verify_transaction_counts(name: String, chain: BlockChain) { "jsonrpc": "2.0", "method": "eth_getBlockTransactionCountByHash", "params": [ - ""#.to_owned() + format!("0x{:?}", hash).as_ref() + r#"" + ""#.to_owned() + format!("0x{:x}", hash).as_ref() + r#"" ], "id": "# + format!("{}", *id).as_ref() + r#" }"#; @@ -437,7 +423,7 @@ fn verify_transaction_counts(name: String, chain: BlockChain) { let tester = EthTester::from_chain(&chain); let mut id = 1; - for b in chain.blocks_rlp().iter().filter(|b| Block::is_good(b)).map(|b| BlockView::new(b)) { + for b in chain.blocks_rlp().iter().filter(|b| Block::is_good(b)).map(|b| view!(BlockView, b)) { let count = b.transactions_count(); let hash = b.hash(); @@ -463,7 +449,7 @@ fn starting_nonce_test() { { "jsonrpc": "2.0", "method": "eth_getTransactionCount", - "params": [""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"", "latest"], + "params": [""#.to_owned() + format!("0x{:x}", address).as_ref() + r#"", "latest"], "id": 15 } "#) diff --git a/rpc/src/v1/tests/helpers/dapps.rs b/rpc/src/v1/tests/helpers/dapps.rs index 10c54cf4c2476324e53b21b303c5f88a68d29e8b..70f42a29e50ecf534afef980844c3e907346d4dd 100644 --- a/rpc/src/v1/tests/helpers/dapps.rs +++ b/rpc/src/v1/tests/helpers/dapps.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/tests/helpers/fetch.rs b/rpc/src/v1/tests/helpers/fetch.rs deleted file mode 100644 index 316d0682554c5f3f2ca4d42eedabae68d2f90c50..0000000000000000000000000000000000000000 --- a/rpc/src/v1/tests/helpers/fetch.rs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2015-2017 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 . - -//! Test implementation of fetch client. - -use std::thread; -use jsonrpc_core::futures::{self, Future}; -use fetch::{self, Fetch, Url}; -use hyper; - -/// Test implementation of fetcher. Will always return the same file. -#[derive(Default, Clone)] -pub struct TestFetch; - -impl Fetch for TestFetch { - type Result = Box + Send + 'static>; - - fn fetch(&self, url: &str, abort: fetch::Abort) -> Self::Result { - let u = Url::parse(url).unwrap(); - let (tx, rx) = futures::oneshot(); - thread::spawn(move || { - let r = hyper::Response::new().with_body(&b"Some content"[..]); - tx.send(fetch::Response::new(u, r, abort)).unwrap(); - }); - - Box::new(rx.map_err(|_| fetch::Error::Aborted)) - } -} diff --git a/rpc/src/v1/tests/helpers/miner_service.rs b/rpc/src/v1/tests/helpers/miner_service.rs index ca9993baea40140e5a92c52def9f11ae6cceaaab..63c28fb6cc4f00df0fae55c53f3ddd6c5bf8d11b 100644 --- a/rpc/src/v1/tests/helpers/miner_service.rs +++ b/rpc/src/v1/tests/helpers/miner_service.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -16,82 +16,68 @@ //! Test implementation of miner service. +use std::sync::Arc; use std::collections::{BTreeMap, HashMap}; -use std::collections::hash_map::Entry; use bytes::Bytes; use ethcore::account_provider::SignError as AccountError; -use ethcore::block::{Block, ClosedBlock}; +use ethcore::block::{Block, SealedBlock, IsBlock}; use ethcore::client::{Nonce, PrepareOpenBlock, StateClient, EngineInfo}; use ethcore::engines::EthEngine; use ethcore::error::Error; use ethcore::header::{BlockNumber, Header}; use ethcore::ids::BlockId; -use ethcore::miner::{MinerService, MinerStatus}; +use ethcore::miner::{self, MinerService, AuthoringParams}; use ethcore::receipt::{Receipt, RichReceipt}; use ethereum_types::{H256, U256, Address}; -use miner::local_transactions::Status as LocalTransactionStatus; +use miner::pool::local_transactions::Status as LocalTransactionStatus; +use miner::pool::{verifier, VerifiedTransaction, QueueStatus}; use parking_lot::{RwLock, Mutex}; -use transaction::{UnverifiedTransaction, SignedTransaction, PendingTransaction, ImportResult as TransactionImportResult}; +use transaction::{self, UnverifiedTransaction, SignedTransaction, PendingTransaction}; +use txpool; /// Test miner service. pub struct TestMinerService { /// Imported transactions. pub imported_transactions: Mutex>, - /// Latest closed block. - pub latest_closed_block: Mutex>, /// Pre-existed pending transactions pub pending_transactions: Mutex>, /// Pre-existed local transactions pub local_transactions: Mutex>, /// Pre-existed pending receipts pub pending_receipts: Mutex>, - /// Last nonces. - pub last_nonces: RwLock>, + /// Next nonces. + pub next_nonces: RwLock>, /// Password held by Engine. pub password: RwLock, - min_gas_price: RwLock, - gas_range_target: RwLock<(U256, U256)>, - author: RwLock
, - extra_data: RwLock, - limit: RwLock, - tx_gas_limit: RwLock, + authoring_params: RwLock, } impl Default for TestMinerService { fn default() -> TestMinerService { TestMinerService { imported_transactions: Mutex::new(Vec::new()), - latest_closed_block: Mutex::new(None), pending_transactions: Mutex::new(HashMap::new()), local_transactions: Mutex::new(BTreeMap::new()), pending_receipts: Mutex::new(BTreeMap::new()), - last_nonces: RwLock::new(HashMap::new()), - min_gas_price: RwLock::new(U256::from(20_000_000)), - gas_range_target: RwLock::new((U256::from(12345), U256::from(54321))), - author: RwLock::new(Address::zero()), + next_nonces: RwLock::new(HashMap::new()), password: RwLock::new(String::new()), - extra_data: RwLock::new(vec![1, 2, 3, 4]), - limit: RwLock::new(1024), - tx_gas_limit: RwLock::new(!U256::zero()), + authoring_params: RwLock::new(AuthoringParams { + author: Address::zero(), + gas_range_target: (12345.into(), 54321.into()), + extra_data: vec![1, 2, 3, 4], + }), } } } impl TestMinerService { - /// Increments last nonce for given address. - pub fn increment_last_nonce(&self, address: Address) { - let mut last_nonces = self.last_nonces.write(); - match last_nonces.entry(address) { - Entry::Occupied(mut occupied) => { - let val = *occupied.get(); - *occupied.get_mut() = val + 1.into(); - }, - Entry::Vacant(vacant) => { - vacant.insert(0.into()); - }, - } + /// Increments nonce for given address. + pub fn increment_nonce(&self, address: &Address) { + let mut next_nonces = self.next_nonces.write(); + let nonce = next_nonces.entry(*address).or_insert_with(|| 0.into()); + *nonce = *nonce + 1.into(); } } @@ -129,164 +115,119 @@ impl MinerService for TestMinerService { None } - /// Returns miner's status. - fn status(&self) -> MinerStatus { - MinerStatus { - transactions_in_pending_queue: 0, - transactions_in_future_queue: 0, - transactions_in_pending_block: 1 - } - } - - fn set_author(&self, author: Address) { - *self.author.write() = author; + fn authoring_params(&self) -> AuthoringParams { + self.authoring_params.read().clone() } - fn set_engine_signer(&self, address: Address, password: String) -> Result<(), AccountError> { - *self.author.write() = address; - *self.password.write() = password; + fn set_author(&self, author: Address, password: Option) -> Result<(), AccountError> { + self.authoring_params.write().author = author; + if let Some(password) = password { + *self.password.write() = password; + } Ok(()) } fn set_extra_data(&self, extra_data: Bytes) { - *self.extra_data.write() = extra_data; - } - - /// Set the lower gas limit we wish to target when sealing a new block. - fn set_gas_floor_target(&self, target: U256) { - self.gas_range_target.write().0 = target; - } - - /// Set the upper gas limit we wish to target when sealing a new block. - fn set_gas_ceil_target(&self, target: U256) { - self.gas_range_target.write().1 = target; - } - - fn set_minimal_gas_price(&self, min_gas_price: U256) { - *self.min_gas_price.write() = min_gas_price; - } - - fn set_transactions_limit(&self, limit: usize) { - *self.limit.write() = limit; - } - - fn set_tx_gas_limit(&self, limit: U256) { - *self.tx_gas_limit.write() = limit; - } - - fn transactions_limit(&self) -> usize { - *self.limit.read() - } - - fn author(&self) -> Address { - *self.author.read() - } - - fn minimal_gas_price(&self) -> U256 { - *self.min_gas_price.read() - } - - fn extra_data(&self) -> Bytes { - self.extra_data.read().clone() - } - - fn gas_floor_target(&self) -> U256 { - self.gas_range_target.read().0 + self.authoring_params.write().extra_data = extra_data; } - fn gas_ceil_target(&self) -> U256 { - self.gas_range_target.read().1 + fn set_gas_range_target(&self, target: (U256, U256)) { + self.authoring_params.write().gas_range_target = target; } /// Imports transactions to transaction queue. - fn import_external_transactions(&self, _chain: &C, transactions: Vec) -> - Vec> { + fn import_external_transactions(&self, chain: &C, transactions: Vec) + -> Vec> + { // lets assume that all txs are valid let transactions: Vec<_> = transactions.into_iter().map(|tx| SignedTransaction::new(tx).unwrap()).collect(); self.imported_transactions.lock().extend_from_slice(&transactions); for sender in transactions.iter().map(|tx| tx.sender()) { - let nonce = self.last_nonce(&sender).expect("last_nonce must be populated in tests"); - self.last_nonces.write().insert(sender, nonce + U256::from(1)); + let nonce = self.next_nonce(chain, &sender); + self.next_nonces.write().insert(sender, nonce); } + transactions .iter() - .map(|_| Ok(TransactionImportResult::Current)) + .map(|_| Ok(())) .collect() } /// Imports transactions to transaction queue. - fn import_own_transaction(&self, chain: &C, pending: PendingTransaction) -> - Result { + fn import_own_transaction(&self, _chain: &C, _pending: PendingTransaction) + -> Result<(), transaction::Error> { + // this function is no longer called directly from RPC + unimplemented!(); + } + + /// Imports transactions to queue - treats as local based on trusted flag, config, and tx source + fn import_claimed_local_transaction(&self, chain: &C, pending: PendingTransaction, _trusted: bool) + -> Result<(), transaction::Error> { // keep the pending nonces up to date let sender = pending.transaction.sender(); - let nonce = self.last_nonce(&sender).unwrap_or(chain.latest_nonce(&sender)); - self.last_nonces.write().insert(sender, nonce + U256::from(1)); + let nonce = self.next_nonce(chain, &sender); + self.next_nonces.write().insert(sender, nonce); // lets assume that all txs are valid self.imported_transactions.lock().push(pending.transaction); - Ok(TransactionImportResult::Current) - } - - /// Returns hashes of transactions currently in pending - fn pending_transactions_hashes(&self, _best_block: BlockNumber) -> Vec { - vec![] - } - - /// Removes all transactions from the queue and restart mining operation. - fn clear_and_reset(&self, _chain: &C) { - unimplemented!(); + Ok(()) } /// Called when blocks are imported to chain, updates transactions queue. - fn chain_new_blocks(&self, _chain: &C, _imported: &[H256], _invalid: &[H256], _enacted: &[H256], _retracted: &[H256]) { + fn chain_new_blocks(&self, _chain: &C, _imported: &[H256], _invalid: &[H256], _enacted: &[H256], _retracted: &[H256], _is_internal: bool) { unimplemented!(); } - /// PoW chain - can produce work package - fn can_produce_work_package(&self) -> bool { - true - } - /// New chain head event. Restart mining operation. fn update_sealing(&self, _chain: &C) { unimplemented!(); } - fn map_sealing_work(&self, chain: &C, f: F) -> Option where F: FnOnce(&ClosedBlock) -> T { - let open_block = chain.prepare_open_block(self.author(), *self.gas_range_target.write(), self.extra_data()); - Some(f(&open_block.close())) + fn work_package(&self, chain: &C) -> Option<(H256, BlockNumber, u64, U256)> { + let params = self.authoring_params(); + let open_block = chain.prepare_open_block(params.author, params.gas_range_target, params.extra_data); + let closed = open_block.close(); + let header = closed.header(); + + Some((header.hash(), header.number(), header.timestamp(), *header.difficulty())) } - fn transaction(&self, _best_block: BlockNumber, hash: &H256) -> Option { - self.pending_transactions.lock().get(hash).cloned().map(Into::into) + fn transaction(&self, hash: &H256) -> Option> { + self.pending_transactions.lock().get(hash).cloned().map(|tx| { + Arc::new(VerifiedTransaction::from_pending_block_transaction(tx)) + }) } - fn remove_pending_transaction(&self, _chain: &C, hash: &H256) -> Option { - self.pending_transactions.lock().remove(hash).map(Into::into) + fn remove_transaction(&self, hash: &H256) -> Option> { + self.pending_transactions.lock().remove(hash).map(|tx| { + Arc::new(VerifiedTransaction::from_pending_block_transaction(tx)) + }) } - fn pending_transactions(&self) -> Vec { - self.pending_transactions.lock().values().cloned().map(Into::into).collect() + fn pending_transactions(&self, _best_block: BlockNumber) -> Option> { + Some(self.pending_transactions.lock().values().cloned().collect()) } fn local_transactions(&self) -> BTreeMap { self.local_transactions.lock().iter().map(|(hash, stats)| (*hash, stats.clone())).collect() } - fn ready_transactions(&self, _best_block: BlockNumber, _best_timestamp: u64) -> Vec { - self.pending_transactions.lock().values().cloned().map(Into::into).collect() + fn ready_transactions(&self, _chain: &C, _max_len: usize, _ordering: miner::PendingOrdering) -> Vec> { + self.queued_transactions() } - fn future_transactions(&self) -> Vec { - vec![] + fn queued_transactions(&self) -> Vec> { + self.pending_transactions.lock().values().cloned().map(|tx| { + Arc::new(VerifiedTransaction::from_pending_block_transaction(tx)) + }).collect() } fn pending_receipt(&self, _best_block: BlockNumber, hash: &H256) -> Option { // Not much point implementing this since the logic is complex and the only thing it relies on is pending_receipts, which is already tested. - self.pending_receipts(0).get(hash).map(|r| + self.pending_receipts(0).unwrap().get(hash).map(|r| RichReceipt { transaction_hash: Default::default(), transaction_index: Default::default(), @@ -300,25 +241,49 @@ impl MinerService for TestMinerService { ) } - fn pending_receipts(&self, _best_block: BlockNumber) -> BTreeMap { - self.pending_receipts.lock().clone() + fn pending_receipts(&self, _best_block: BlockNumber) -> Option> { + Some(self.pending_receipts.lock().clone()) } - fn last_nonce(&self, address: &Address) -> Option { - self.last_nonces.read().get(address).cloned() + fn next_nonce(&self, _chain: &C, address: &Address) -> U256 { + self.next_nonces.read().get(address).cloned().unwrap_or_default() } fn is_currently_sealing(&self) -> bool { false } + fn queue_status(&self) -> QueueStatus { + QueueStatus { + options: verifier::Options { + minimal_gas_price: 0x1312d00.into(), + block_gas_limit: 5_000_000.into(), + tx_gas_limit: 5_000_000.into(), + }, + status: txpool::LightStatus { + mem_usage: 1_000, + transaction_count: 52, + senders: 1, + }, + limits: txpool::Options { + max_count: 1_024, + max_per_sender: 16, + max_mem_usage: 5_000, + }, + } + } + /// Submit `seal` as a valid solution for the header of `pow_hash`. /// Will check the seal, but not actually insert the block into the chain. - fn submit_seal(&self, _chain: &C, _pow_hash: H256, _seal: Vec) -> Result<(), Error> { + fn submit_seal(&self, _pow_hash: H256, _seal: Vec) -> Result { unimplemented!(); } fn sensible_gas_price(&self) -> U256 { - 20000000000u64.into() + 20_000_000_000u64.into() + } + + fn sensible_gas_limit(&self) -> U256 { + 0x5208.into() } } diff --git a/rpc/src/v1/tests/helpers/mod.rs b/rpc/src/v1/tests/helpers/mod.rs index aae48a2d2806201593ca4246d0f34cd0abbc1074..a2782eec60cbd54a6995bd1e6d30ded46a008c23 100644 --- a/rpc/src/v1/tests/helpers/mod.rs +++ b/rpc/src/v1/tests/helpers/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -17,14 +17,12 @@ //! Test rpc services. mod dapps; -mod fetch; mod miner_service; mod snapshot_service; mod sync_provider; mod update_service; pub use self::dapps::TestDappsService; -pub use self::fetch::TestFetch; pub use self::miner_service::TestMinerService; pub use self::snapshot_service::TestSnapshotService; pub use self::sync_provider::{Config, TestSyncProvider}; diff --git a/rpc/src/v1/tests/helpers/snapshot_service.rs b/rpc/src/v1/tests/helpers/snapshot_service.rs index 25abab578e9df70fc468b70d864d1ae85db2e406..4e45488dbe519af47408fffbeaca6821c404a45f 100644 --- a/rpc/src/v1/tests/helpers/snapshot_service.rs +++ b/rpc/src/v1/tests/helpers/snapshot_service.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -43,10 +43,12 @@ impl TestSnapshotService { impl SnapshotService for TestSnapshotService { fn manifest(&self) -> Option { None } fn supported_versions(&self) -> Option<(u64, u64)> { None } + fn completed_chunks(&self) -> Option> { Some(vec![]) } fn chunk(&self, _hash: H256) -> Option { None } fn status(&self) -> RestorationStatus { self.status.lock().clone() } fn begin_restore(&self, _manifest: ManifestData) { } fn abort_restore(&self) { } fn restore_state_chunk(&self, _hash: H256, _chunk: Bytes) { } fn restore_block_chunk(&self, _hash: H256, _chunk: Bytes) { } + fn shutdown(&self) { } } diff --git a/rpc/src/v1/tests/helpers/sync_provider.rs b/rpc/src/v1/tests/helpers/sync_provider.rs index cbe66542352e1773449e120527b06e268cf8697b..7cb0acffefce1510579545d1e52d2de45892d22c 100644 --- a/rpc/src/v1/tests/helpers/sync_provider.rs +++ b/rpc/src/v1/tests/helpers/sync_provider.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -19,7 +19,7 @@ use std::collections::BTreeMap; use ethereum_types::H256; use parking_lot::RwLock; -use ethsync::{SyncProvider, EthProtocolInfo, SyncStatus, SyncState, PeerInfo, TransactionStats}; +use sync::{SyncProvider, EthProtocolInfo, SyncStatus, SyncState, PeerInfo, TransactionStats}; /// TestSyncProvider config. pub struct Config { @@ -123,4 +123,3 @@ impl SyncProvider for TestSyncProvider { ] } } - diff --git a/rpc/src/v1/tests/helpers/update_service.rs b/rpc/src/v1/tests/helpers/update_service.rs index eaa3b06fbea3c0d8fab241ffc017d869424c19b3..3c4d0b1d7de12de46bba8c884e8ab4ed56306421 100644 --- a/rpc/src/v1/tests/helpers/update_service.rs +++ b/rpc/src/v1/tests/helpers/update_service.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/tests/mocked/eth.rs b/rpc/src/v1/tests/mocked/eth.rs index e901993bddda0e0e454ac3bf0e5afa7b2df41ab8..87ca18fe24146233a5c33a3dda9a8fb629bc22a1 100644 --- a/rpc/src/v1/tests/mocked/eth.rs +++ b/rpc/src/v1/tests/mocked/eth.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -19,7 +19,7 @@ use std::collections::HashMap; use std::sync::Arc; use std::time::{Instant, Duration, SystemTime, UNIX_EPOCH}; -use ethereum_types::{H256, U256, Address}; +use ethereum_types::{H160, H256, U256, Address}; use parking_lot::Mutex; use ethcore::account_provider::AccountProvider; use ethcore::client::{BlockChainClient, BlockId, EachBlockWith, Executed, TestBlockChainClient, TransactionId}; @@ -27,7 +27,7 @@ use ethcore::log_entry::{LocalizedLogEntry, LogEntry}; use ethcore::miner::MinerService; use ethcore::receipt::{LocalizedReceipt, TransactionOutcome}; use ethkey::Secret; -use ethsync::SyncState; +use sync::SyncState; use miner::external::ExternalMiner; use rlp; use rustc_hex::{FromHex, ToHex}; @@ -86,14 +86,15 @@ impl EthTester { let client = blockchain_client(); let sync = sync_provider(); let ap = accounts_provider(); - let opt_ap = Some(ap.clone()); + let opt_ap = ap.clone(); let miner = miner_service(); let snapshot = snapshot_service(); let hashrates = Arc::new(Mutex::new(HashMap::new())); let external_miner = Arc::new(ExternalMiner::new(hashrates.clone())); let gas_price_percentile = options.gas_price_percentile; + let poll_lifetime = options.poll_lifetime; let eth = EthClient::new(&client, &snapshot, &sync, &opt_ap, &miner, &external_miner, options).to_delegate(); - let filter = EthFilterClient::new(client.clone(), miner.clone()).to_delegate(); + let filter = EthFilterClient::new(client.clone(), miner.clone(), poll_lifetime).to_delegate(); let reservations = Arc::new(Mutex::new(nonce::Reservations::new())); let dispatcher = FullDispatcher::new(client.clone(), miner.clone(), reservations, gas_price_percentile); @@ -149,7 +150,6 @@ fn rpc_eth_syncing() { // causes TestBlockChainClient to return 1000 for its best block number. tester.add_blocks(1000, EachBlockWith::Nothing); - let true_res = r#"{"jsonrpc":"2.0","result":{"currentBlock":"0x3e8","highestBlock":"0x9c4","startingBlock":"0x0","warpChunksAmount":null,"warpChunksProcessed":null},"id":1}"#; assert_eq!(tester.io.handle_request_sync(request), Some(true_res.to_owned())); @@ -221,13 +221,12 @@ fn rpc_eth_logs() { log_index: 1, }]); - let request1 = r#"{"jsonrpc": "2.0", "method": "eth_getLogs", "params": [{}], "id": 1}"#; let request2 = r#"{"jsonrpc": "2.0", "method": "eth_getLogs", "params": [{"limit":1}], "id": 1}"#; let request3 = r#"{"jsonrpc": "2.0", "method": "eth_getLogs", "params": [{"limit":0}], "id": 1}"#; - let response1 = r#"{"jsonrpc":"2.0","result":[{"address":"0x0000000000000000000000000000000000000000","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":"0x1","data":"0x010203","logIndex":"0x0","topics":[],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","transactionLogIndex":"0x0","type":"mined"},{"address":"0x0000000000000000000000000000000000000000","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":"0x1","data":"0x010203","logIndex":"0x1","topics":[],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","transactionLogIndex":"0x1","type":"mined"}],"id":1}"#; - let response2 = r#"{"jsonrpc":"2.0","result":[{"address":"0x0000000000000000000000000000000000000000","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":"0x1","data":"0x010203","logIndex":"0x1","topics":[],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","transactionLogIndex":"0x1","type":"mined"}],"id":1}"#; + let response1 = r#"{"jsonrpc":"2.0","result":[{"address":"0x0000000000000000000000000000000000000000","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":"0x1","data":"0x010203","logIndex":"0x0","removed":false,"topics":[],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","transactionLogIndex":"0x0","type":"mined"},{"address":"0x0000000000000000000000000000000000000000","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":"0x1","data":"0x010203","logIndex":"0x1","removed":false,"topics":[],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","transactionLogIndex":"0x1","type":"mined"}],"id":1}"#; + let response2 = r#"{"jsonrpc":"2.0","result":[{"address":"0x0000000000000000000000000000000000000000","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":"0x1","data":"0x010203","logIndex":"0x1","removed":false,"topics":[],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","transactionLogIndex":"0x1","type":"mined"}],"id":1}"#; let response3 = r#"{"jsonrpc":"2.0","result":[],"id":1}"#; assert_eq!(tester.io.handle_request_sync(request1), Some(response1.to_owned())); @@ -276,8 +275,8 @@ fn rpc_logs_filter() { let request_changes1 = r#"{"jsonrpc": "2.0", "method": "eth_getFilterChanges", "params": ["0x0"], "id": 1}"#; let request_changes2 = r#"{"jsonrpc": "2.0", "method": "eth_getFilterChanges", "params": ["0x1"], "id": 1}"#; - let response1 = r#"{"jsonrpc":"2.0","result":[{"address":"0x0000000000000000000000000000000000000000","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":"0x1","data":"0x010203","logIndex":"0x0","topics":[],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","transactionLogIndex":"0x0","type":"mined"},{"address":"0x0000000000000000000000000000000000000000","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":"0x1","data":"0x010203","logIndex":"0x1","topics":[],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","transactionLogIndex":"0x1","type":"mined"}],"id":1}"#; - let response2 = r#"{"jsonrpc":"2.0","result":[{"address":"0x0000000000000000000000000000000000000000","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":"0x1","data":"0x010203","logIndex":"0x1","topics":[],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","transactionLogIndex":"0x1","type":"mined"}],"id":1}"#; + let response1 = r#"{"jsonrpc":"2.0","result":[{"address":"0x0000000000000000000000000000000000000000","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":"0x1","data":"0x010203","logIndex":"0x0","removed":false,"topics":[],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","transactionLogIndex":"0x0","type":"mined"},{"address":"0x0000000000000000000000000000000000000000","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":"0x1","data":"0x010203","logIndex":"0x1","removed":false,"topics":[],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","transactionLogIndex":"0x1","type":"mined"}],"id":1}"#; + let response2 = r#"{"jsonrpc":"2.0","result":[{"address":"0x0000000000000000000000000000000000000000","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":"0x1","data":"0x010203","logIndex":"0x1","removed":false,"topics":[],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","transactionLogIndex":"0x1","type":"mined"}],"id":1}"#; assert_eq!(tester.io.handle_request_sync(request_changes1), Some(response1.to_owned())); assert_eq!(tester.io.handle_request_sync(request_changes2), Some(response2.to_owned())); @@ -329,7 +328,7 @@ fn rpc_eth_submit_hashrate() { fn rpc_eth_sign() { let tester = EthTester::default(); - let account = tester.accounts_provider.insert_account(Secret::from_slice(&[69u8; 32]), "abcd").unwrap(); + let account = tester.accounts_provider.insert_account(Secret::from([69u8; 32]), "abcd").unwrap(); tester.accounts_provider.unlock_account_permanently(account, "abcd".into()).unwrap(); let _message = "0cc175b9c0f1b6a831c399e26977266192eb5ffee6ae2fec3ad71c777531578f".from_hex().unwrap(); @@ -337,7 +336,7 @@ fn rpc_eth_sign() { "jsonrpc": "2.0", "method": "eth_sign", "params": [ - ""#.to_owned() + &format!("0x{:?}", account) + r#"", + ""#.to_owned() + &format!("0x{:x}", account) + r#"", "0x0cc175b9c0f1b6a831c399e26977266192eb5ffee6ae2fec3ad71c777531578f" ], "id": 1 @@ -349,7 +348,7 @@ fn rpc_eth_sign() { #[test] fn rpc_eth_author() { - let make_res = |addr| r#"{"jsonrpc":"2.0","result":""#.to_owned() + &format!("0x{:?}", addr) + r#"","id":1}"#; + let make_res = |addr| r#"{"jsonrpc":"2.0","result":""#.to_owned() + &format!("0x{:x}", addr) + r#"","id":1}"#; let tester = EthTester::default(); let req = r#"{ @@ -368,7 +367,7 @@ fn rpc_eth_author() { for i in 0..20 { let addr = tester.accounts_provider.new_account(&format!("{}", i)).unwrap(); - tester.miner.set_author(addr.clone()); + tester.miner.set_author(addr.clone(), None).unwrap(); assert_eq!(tester.io.handle_request_sync(req), Some(make_res(addr))); } @@ -377,7 +376,7 @@ fn rpc_eth_author() { #[test] fn rpc_eth_mining() { let tester = EthTester::default(); - tester.miner.set_author(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()); + tester.miner.set_author(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap(), None).unwrap(); let request = r#"{"jsonrpc": "2.0", "method": "eth_mining", "params": [], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","result":false,"id":1}"#; @@ -402,7 +401,7 @@ fn rpc_eth_accounts() { // with current policy it should return the account let request = r#"{"jsonrpc": "2.0", "method": "eth_accounts", "params": [], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":[""#.to_owned() + &format!("0x{:?}", address) + r#""],"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":[""#.to_owned() + &format!("0x{:x}", address) + r#""],"id":1}"#; assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); tester.accounts_provider.set_new_dapps_addresses(Some(vec![1.into()])).unwrap(); @@ -498,7 +497,7 @@ fn rpc_eth_transaction_count_next_nonce() { let tester = EthTester::new_with_options(EthClientOptions::with(|options| { options.pending_nonce_from_queue = true; })); - tester.miner.increment_last_nonce(1.into()); + tester.miner.increment_nonce(&1.into()); let request1 = r#"{ "jsonrpc": "2.0", @@ -553,7 +552,7 @@ fn rpc_eth_transaction_count_by_number_pending() { "params": ["pending"], "id": 1 }"#; - let response = r#"{"jsonrpc":"2.0","result":"0x1","id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":"0x0","id":1}"#; assert_eq!(EthTester::default().io.handle_request_sync(request), Some(response.to_owned())); } @@ -566,7 +565,8 @@ fn rpc_eth_pending_transaction_by_hash() { let tester = EthTester::default(); { - let tx = rlp::decode(&FromHex::from_hex("f85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804").unwrap()); + let bytes = FromHex::from_hex("f85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804").unwrap(); + let tx = rlp::decode(&bytes).expect("decoding failure"); let tx = SignedTransaction::new(tx).unwrap(); tester.miner.pending_transactions.lock().insert(H256::zero(), tx); } @@ -581,7 +581,6 @@ fn rpc_eth_pending_transaction_by_hash() { assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); } - #[test] fn rpc_eth_uncle_count_by_block_hash() { let request = r#"{ @@ -811,7 +810,7 @@ fn rpc_eth_send_transaction() { "jsonrpc": "2.0", "method": "eth_sendTransaction", "params": [{ - "from": ""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"", + "from": ""#.to_owned() + format!("0x{:x}", address).as_ref() + r#"", "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", "gas": "0x76c0", "gasPrice": "0x9184e72a000", @@ -831,11 +830,11 @@ fn rpc_eth_send_transaction() { let signature = tester.accounts_provider.sign(address, None, t.hash(None)).unwrap(); let t = t.with_signature(signature, None); - let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:?}", t.hash()).as_ref() + r#"","id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:x}", t.hash()).as_ref() + r#"","id":1}"#; assert_eq!(tester.io.handle_request_sync(&request), Some(response)); - tester.miner.last_nonces.write().insert(address.clone(), U256::zero()); + tester.miner.increment_nonce(&address); let t = Transaction { nonce: U256::one(), @@ -848,7 +847,7 @@ fn rpc_eth_send_transaction() { let signature = tester.accounts_provider.sign(address, None, t.hash(None)).unwrap(); let t = t.with_signature(signature, None); - let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:?}", t.hash()).as_ref() + r#"","id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:x}", t.hash()).as_ref() + r#"","id":1}"#; assert_eq!(tester.io.handle_request_sync(&request), Some(response)); } @@ -862,7 +861,7 @@ fn rpc_eth_sign_transaction() { "jsonrpc": "2.0", "method": "eth_signTransaction", "params": [{ - "from": ""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"", + "from": ""#.to_owned() + format!("0x{:x}", address).as_ref() + r#"", "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", "gas": "0x76c0", "gasPrice": "0x9184e72a000", @@ -890,12 +889,12 @@ fn rpc_eth_sign_transaction() { r#""blockHash":null,"blockNumber":null,"# + &format!("\"chainId\":{},", t.chain_id().map_or("null".to_owned(), |n| format!("{}", n))) + r#""condition":null,"creates":null,"# + - &format!("\"from\":\"0x{:?}\",", &address) + + &format!("\"from\":\"0x{:x}\",", &address) + r#""gas":"0x76c0","gasPrice":"0x9184e72a000","# + - &format!("\"hash\":\"0x{:?}\",", t.hash()) + + &format!("\"hash\":\"0x{:x}\",", t.hash()) + r#""input":"0x","# + r#""nonce":"0x1","# + - &format!("\"publicKey\":\"0x{:?}\",", t.recover_public().unwrap()) + + &format!("\"publicKey\":\"0x{:x}\",", t.recover_public().unwrap()) + &format!("\"r\":\"0x{:x}\",", U256::from(signature.r())) + &format!("\"raw\":\"0x{}\",", rlp.to_hex()) + &format!("\"s\":\"0x{:x}\",", U256::from(signature.s())) + @@ -905,7 +904,7 @@ fn rpc_eth_sign_transaction() { r#""value":"0x9184e72a""# + r#"}},"id":1}"#; - tester.miner.last_nonces.write().insert(address.clone(), U256::zero()); + tester.miner.increment_nonce(&address); assert_eq!(tester.io.handle_request_sync(&request), Some(response)); } @@ -918,7 +917,7 @@ fn rpc_eth_send_transaction_with_bad_to() { "jsonrpc": "2.0", "method": "eth_sendTransaction", "params": [{ - "from": ""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"", + "from": ""#.to_owned() + format!("0x{:x}", address).as_ref() + r#"", "to": "", "gas": "0x76c0", "gasPrice": "0x9184e72a000", @@ -932,7 +931,6 @@ fn rpc_eth_send_transaction_with_bad_to() { assert_eq!(tester.io.handle_request_sync(&request), Some(response.into())); } - #[test] fn rpc_eth_send_transaction_error() { let tester = EthTester::default(); @@ -941,7 +939,7 @@ fn rpc_eth_send_transaction_error() { "jsonrpc": "2.0", "method": "eth_sendTransaction", "params": [{ - "from": ""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"", + "from": ""#.to_owned() + format!("0x{:x}", address).as_ref() + r#"", "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", "gas": "0x76c0", "gasPrice": "0x9184e72a000", @@ -999,7 +997,7 @@ fn rpc_eth_send_raw_transaction() { "id": 1 }"#; - let res = r#"{"jsonrpc":"2.0","result":""#.to_owned() + &format!("0x{:?}", t.hash()) + r#"","id":1}"#; + let res = r#"{"jsonrpc":"2.0","result":""#.to_owned() + &format!("0x{:x}", t.hash()) + r#"","id":1}"#; assert_eq!(tester.io.handle_request_sync(&req), Some(res)); } @@ -1007,6 +1005,8 @@ fn rpc_eth_send_raw_transaction() { #[test] fn rpc_eth_transaction_receipt() { let receipt = LocalizedReceipt { + from: H160::from_str("b60e8dd61c5d32be8058bb8eb970870f07233155").unwrap(), + to: Some(H160::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), transaction_hash: H256::zero(), transaction_index: 0, block_hash: H256::from_str("ed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5").unwrap(), @@ -1044,7 +1044,7 @@ fn rpc_eth_transaction_receipt() { "params": ["0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238"], "id": 1 }"#; - let response = r#"{"jsonrpc":"2.0","result":{"blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x4510c","contractAddress":null,"cumulativeGasUsed":"0x20","gasUsed":"0x10","logs":[{"address":"0x33990122638b9132ca29c723bdf037f1a891a70c","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x4510c","data":"0x","logIndex":"0x1","topics":["0xa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc","0x4861736852656700000000000000000000000000000000000000000000000000"],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","transactionLogIndex":"0x0","type":"mined"}],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","root":"0x0000000000000000000000000000000000000000000000000000000000000000","status":null,"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0"},"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":{"blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x4510c","contractAddress":null,"cumulativeGasUsed":"0x20","from":"0xb60e8dd61c5d32be8058bb8eb970870f07233155","gasUsed":"0x10","logs":[{"address":"0x33990122638b9132ca29c723bdf037f1a891a70c","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x4510c","data":"0x","logIndex":"0x1","removed":false,"topics":["0xa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc","0x4861736852656700000000000000000000000000000000000000000000000000"],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","transactionLogIndex":"0x0","type":"mined"}],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","root":"0x0000000000000000000000000000000000000000000000000000000000000000","status":null,"to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0"},"id":1}"#; assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); } @@ -1118,7 +1118,7 @@ fn rpc_get_work_returns_no_work_if_cant_mine() { #[test] fn rpc_get_work_returns_correct_work_package() { let eth_tester = EthTester::default(); - eth_tester.miner.set_author(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()); + eth_tester.miner.set_author(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap(), None).unwrap(); let request = r#"{"jsonrpc": "2.0", "method": "eth_getWork", "params": [], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","result":["0x76c7bd86693aee93d1a80a408a09a0585b1a1292afcb56192f171d925ea18e2d","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000800000000000000000000000000000000000000000000000000000000000","0x1"],"id":1}"#; @@ -1131,7 +1131,7 @@ fn rpc_get_work_should_not_return_block_number() { let eth_tester = EthTester::new_with_options(EthClientOptions::with(|options| { options.send_block_number_in_get_work = false; })); - eth_tester.miner.set_author(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()); + eth_tester.miner.set_author(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap(), None).unwrap(); let request = r#"{"jsonrpc": "2.0", "method": "eth_getWork", "params": [], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","result":["0x76c7bd86693aee93d1a80a408a09a0585b1a1292afcb56192f171d925ea18e2d","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000800000000000000000000000000000000000000000000000000000000000"],"id":1}"#; @@ -1142,15 +1142,15 @@ fn rpc_get_work_should_not_return_block_number() { #[test] fn rpc_get_work_should_timeout() { let eth_tester = EthTester::default(); - eth_tester.miner.set_author(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()); + eth_tester.miner.set_author(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap(), None).unwrap(); let timestamp = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() - 1000; // Set latest block to 1000 seconds ago eth_tester.client.set_latest_block_timestamp(timestamp); - let hash = eth_tester.miner.map_sealing_work(&*eth_tester.client, |b| b.hash()).unwrap(); + let hash = eth_tester.miner.work_package(&*eth_tester.client).unwrap().0; // Request without providing timeout. This should work since we're disabling timeout. let request = r#"{"jsonrpc": "2.0", "method": "eth_getWork", "params": [], "id": 1}"#; let work_response = format!( - r#"{{"jsonrpc":"2.0","result":["0x{:?}","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000800000000000000000000000000000000000000000000000000000000000","0x1"],"id":1}}"#, + r#"{{"jsonrpc":"2.0","result":["0x{:x}","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000800000000000000000000000000000000000000000000000000000000000","0x1"],"id":1}}"#, hash, ); assert_eq!(eth_tester.io.handle_request_sync(request), Some(work_response.to_owned())); @@ -1158,7 +1158,7 @@ fn rpc_get_work_should_timeout() { // Request with timeout of 0 seconds. This should work since we're disabling timeout. let request = r#"{"jsonrpc": "2.0", "method": "eth_getWork", "params": [0], "id": 1}"#; let work_response = format!( - r#"{{"jsonrpc":"2.0","result":["0x{:?}","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000800000000000000000000000000000000000000000000000000000000000","0x1"],"id":1}}"#, + r#"{{"jsonrpc":"2.0","result":["0x{:x}","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000800000000000000000000000000000000000000000000000000000000000","0x1"],"id":1}}"#, hash, ); assert_eq!(eth_tester.io.handle_request_sync(request), Some(work_response.to_owned())); diff --git a/rpc/src/v1/tests/mocked/eth_pubsub.rs b/rpc/src/v1/tests/mocked/eth_pubsub.rs index 1a0186c97e88404ee2e84bc9c4adb7a8be5b9f76..9233435bf41c7b276cf770c21a25221f671ba733 100644 --- a/rpc/src/v1/tests/mocked/eth_pubsub.rs +++ b/rpc/src/v1/tests/mocked/eth_pubsub.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -20,11 +20,15 @@ use jsonrpc_core::MetaIoHandler; use jsonrpc_core::futures::{self, Stream, Future}; use jsonrpc_pubsub::Session; +use std::time::Duration; + use v1::{EthPubSub, EthPubSubClient, Metadata}; -use ethcore::client::{TestBlockChainClient, EachBlockWith, ChainNotify}; +use ethcore::client::{TestBlockChainClient, EachBlockWith, ChainNotify, ChainRoute, ChainRouteType}; use parity_reactor::EventLoop; +const DURATION_ZERO: Duration = Duration::from_millis(0); + #[test] fn should_subscribe_to_new_heads() { // given @@ -53,13 +57,13 @@ fn should_subscribe_to_new_heads() { assert_eq!(io.handle_request_sync(request, metadata.clone()), Some(response.to_owned())); // Check notifications - handler.new_blocks(vec![], vec![], vec![h1], vec![], vec![], vec![], 0); + handler.new_blocks(vec![], vec![], ChainRoute::new(vec![(h1, ChainRouteType::Enacted)]), vec![], vec![], DURATION_ZERO); let (res, receiver) = receiver.into_future().wait().unwrap(); let response = r#"{"jsonrpc":"2.0","method":"eth_subscription","params":{"result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x1","extraData":"0x","gasLimit":"0xf4240","gasUsed":"0x0","hash":"0x3457d2fa2e3dd33c78ac681cf542e429becf718859053448748383af67e23218","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","number":"0x1","parentHash":"0x0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sealFields":[],"sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","size":"0x1c9","stateRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","timestamp":"0x0","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"},"subscription":"0x416d77337e24399d"}}"#; assert_eq!(res, Some(response.into())); // Notify about two blocks - handler.new_blocks(vec![], vec![], vec![h2, h3], vec![], vec![], vec![], 0); + handler.new_blocks(vec![], vec![], ChainRoute::new(vec![(h2, ChainRouteType::Enacted), (h3, ChainRouteType::Enacted)]), vec![], vec![], DURATION_ZERO); // Receive both let (res, receiver) = receiver.into_future().wait().unwrap(); @@ -125,22 +129,21 @@ fn should_subscribe_to_logs() { assert_eq!(io.handle_request_sync(request, metadata.clone()), Some(response.to_owned())); // Check notifications (enacted) - handler.new_blocks(vec![], vec![], vec![h1], vec![], vec![], vec![], 0); + handler.new_blocks(vec![], vec![], ChainRoute::new(vec![(h1, ChainRouteType::Enacted)]), vec![], vec![], DURATION_ZERO); let (res, receiver) = receiver.into_future().wait().unwrap(); - let response = r#"{"jsonrpc":"2.0","method":"eth_subscription","params":{"result":{"address":"0x0000000000000000000000000000000000000005","blockHash":"0x3457d2fa2e3dd33c78ac681cf542e429becf718859053448748383af67e23218","blockNumber":"0x1","data":"0x","logIndex":"0x0","topics":["0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000"],"transactionHash":""#.to_owned() - + &format!("0x{:?}", tx_hash) + let response = r#"{"jsonrpc":"2.0","method":"eth_subscription","params":{"result":{"address":"0x0000000000000000000000000000000000000005","blockHash":"0x3457d2fa2e3dd33c78ac681cf542e429becf718859053448748383af67e23218","blockNumber":"0x1","data":"0x","logIndex":"0x0","removed":false,"topics":["0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000"],"transactionHash":""#.to_owned() + + &format!("0x{:x}", tx_hash) + r#"","transactionIndex":"0x0","transactionLogIndex":"0x0","type":"mined"},"subscription":"0x416d77337e24399d"}}"#; assert_eq!(res, Some(response.into())); // Check notifications (retracted) - handler.new_blocks(vec![], vec![], vec![], vec![h1], vec![], vec![], 0); + handler.new_blocks(vec![], vec![], ChainRoute::new(vec![(h1, ChainRouteType::Retracted)]), vec![], vec![], DURATION_ZERO); let (res, receiver) = receiver.into_future().wait().unwrap(); - let response = r#"{"jsonrpc":"2.0","method":"eth_subscription","params":{"result":{"address":"0x0000000000000000000000000000000000000005","blockHash":"0x3457d2fa2e3dd33c78ac681cf542e429becf718859053448748383af67e23218","blockNumber":"0x1","data":"0x","logIndex":"0x0","topics":["0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000"],"transactionHash":""#.to_owned() - + &format!("0x{:?}", tx_hash) + let response = r#"{"jsonrpc":"2.0","method":"eth_subscription","params":{"result":{"address":"0x0000000000000000000000000000000000000005","blockHash":"0x3457d2fa2e3dd33c78ac681cf542e429becf718859053448748383af67e23218","blockNumber":"0x1","data":"0x","logIndex":"0x0","removed":true,"topics":["0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000"],"transactionHash":""#.to_owned() + + &format!("0x{:x}", tx_hash) + r#"","transactionIndex":"0x0","transactionLogIndex":"0x0","type":"removed"},"subscription":"0x416d77337e24399d"}}"#; assert_eq!(res, Some(response.into())); - // And unsubscribe let request = r#"{"jsonrpc": "2.0", "method": "eth_unsubscribe", "params": ["0x416d77337e24399d"], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; @@ -150,7 +153,6 @@ fn should_subscribe_to_logs() { assert_eq!(res, None); } - #[test] fn should_subscribe_to_pending_transactions() { // given @@ -179,7 +181,7 @@ fn should_subscribe_to_pending_transactions() { assert_eq!(io.handle_request_sync(request, metadata.clone()), Some(response.to_owned())); // Send new transactions - handler.new_transactions(&[5.into(), 7.into()]); + handler.notify_new_transactions(&[5.into(), 7.into()]); let (res, receiver) = receiver.into_future().wait().unwrap(); let response = r#"{"jsonrpc":"2.0","method":"eth_subscription","params":{"result":"0x0000000000000000000000000000000000000000000000000000000000000005","subscription":"0x416d77337e24399d"}}"#; diff --git a/rpc/src/v1/tests/mocked/manage_network.rs b/rpc/src/v1/tests/mocked/manage_network.rs index 9438429cdb625c89d855a407573e6219598f6931..a742f03c2f9b8777e3cd29a92fd624be5ef15102 100644 --- a/rpc/src/v1/tests/mocked/manage_network.rs +++ b/rpc/src/v1/tests/mocked/manage_network.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -14,7 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use ethsync::{ManageNetwork, NetworkConfiguration}; +use std::ops::Range; +use sync::ManageNetwork; use self::ethcore_network::{ProtocolId, NetworkContext}; extern crate ethcore_network; @@ -29,6 +30,6 @@ impl ManageNetwork for TestManageNetwork { fn add_reserved_peer(&self, _peer: String) -> Result<(), String> { Ok(()) } fn start_network(&self) {} fn stop_network(&self) {} - fn network_config(&self) -> NetworkConfiguration { NetworkConfiguration::new_local() } + fn num_peers_range(&self) -> Range { 25 .. 51 } fn with_proto_context(&self, _: ProtocolId, _: &mut FnMut(&NetworkContext)) { } } diff --git a/rpc/src/v1/tests/mocked/mod.rs b/rpc/src/v1/tests/mocked/mod.rs index ae51c2be67ecff8174b2c45031cb3777d12c5fe2..a3de3b3b71b977c24af9a18938fe8085c9e29359 100644 --- a/rpc/src/v1/tests/mocked/mod.rs +++ b/rpc/src/v1/tests/mocked/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/tests/mocked/net.rs b/rpc/src/v1/tests/mocked/net.rs index 0f77dfb11a542bab242473273f124a534387c9da..b94bf2b11387014ff02ffb1843dd41ca5b90706c 100644 --- a/rpc/src/v1/tests/mocked/net.rs +++ b/rpc/src/v1/tests/mocked/net.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/tests/mocked/parity.rs b/rpc/src/v1/tests/mocked/parity.rs index 4b75e07fedb00f2eec0e653599684737250a5bc4..4bb653c4d264aeaab0ea2a72f1e576d7dc92e20b 100644 --- a/rpc/src/v1/tests/mocked/parity.rs +++ b/rpc/src/v1/tests/mocked/parity.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -17,13 +17,13 @@ use std::sync::Arc; use ethcore::account_provider::AccountProvider; use ethcore::client::{TestBlockChainClient, Executed}; -use ethcore::miner::LocalTransactionStatus; use ethcore_logger::RotatingLogger; +use ethereum_types::{Address, U256, H256}; use ethstore::ethkey::{Generator, Random}; -use ethsync::ManageNetwork; +use miner::pool::local_transactions::Status as LocalTransactionStatus; use node_health::{self, NodeHealth}; use parity_reactor; -use ethereum_types::{Address, U256, H256}; +use sync::ManageNetwork; use jsonrpc_core::IoHandler; use v1::{Parity, ParityClient}; @@ -81,8 +81,6 @@ impl Dependencies { } pub fn client(&self, signer: Option>) -> TestParityClient { - let opt_accounts = Some(self.accounts.clone()); - ParityClient::new( self.client.clone(), self.miner.clone(), @@ -90,7 +88,7 @@ impl Dependencies { self.updater.clone(), self.network.clone(), self.health.clone(), - opt_accounts.clone(), + self.accounts.clone(), self.logger.clone(), self.settings.clone(), signer, @@ -150,7 +148,6 @@ fn rpc_parity_default_account() { let deps = Dependencies::new(); let io = deps.default_client(); - // Check empty let address = Address::default(); let request = r#"{"jsonrpc": "2.0", "method": "parity_defaultAccount", "params": [], "id": 1}"#; @@ -407,7 +404,7 @@ fn rpc_parity_pending_transactions() { fn rpc_parity_encrypt() { let deps = Dependencies::new(); let io = deps.default_client(); - let key = format!("{:?}", Random.generate().unwrap().public()); + let key = format!("{:x}", Random.generate().unwrap().public()); let request = r#"{"jsonrpc": "2.0", "method": "parity_encryptMessage", "params":["0x"#.to_owned() + &key + r#"", "0x01"], "id": 1}"#; assert!(io.handle_request_sync(&request).unwrap().contains("result"), "Should return success."); @@ -455,13 +452,15 @@ fn rpc_parity_next_nonce() { let address = Address::default(); let io1 = deps.default_client(); let deps = Dependencies::new(); - deps.miner.last_nonces.write().insert(address.clone(), 2.into()); + deps.miner.increment_nonce(&address); + deps.miner.increment_nonce(&address); + deps.miner.increment_nonce(&address); let io2 = deps.default_client(); let request = r#"{ "jsonrpc": "2.0", "method": "parity_nextNonce", - "params": [""#.to_owned() + &format!("0x{:?}", address) + r#""], + "params": [""#.to_owned() + &format!("0x{:x}", address) + r#""], "id": 1 }"#; let response1 = r#"{"jsonrpc":"2.0","result":"0x0","id":1}"#; @@ -486,11 +485,20 @@ fn rpc_parity_transactions_stats() { fn rpc_parity_local_transactions() { let deps = Dependencies::new(); let io = deps.default_client(); - deps.miner.local_transactions.lock().insert(10.into(), LocalTransactionStatus::Pending); - deps.miner.local_transactions.lock().insert(15.into(), LocalTransactionStatus::Future); + let tx = ::transaction::Transaction { + value: 5.into(), + gas: 3.into(), + gas_price: 2.into(), + action: ::transaction::Action::Create, + data: vec![1, 2, 3], + nonce: 0.into(), + }.fake_sign(3.into()); + let tx = Arc::new(::miner::pool::VerifiedTransaction::from_pending_block_transaction(tx)); + deps.miner.local_transactions.lock().insert(10.into(), LocalTransactionStatus::Pending(tx.clone())); + deps.miner.local_transactions.lock().insert(15.into(), LocalTransactionStatus::Pending(tx.clone())); let request = r#"{"jsonrpc": "2.0", "method": "parity_localTransactions", "params":[], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":{"0x000000000000000000000000000000000000000000000000000000000000000a":{"status":"pending"},"0x000000000000000000000000000000000000000000000000000000000000000f":{"status":"future"}},"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":{"0x000000000000000000000000000000000000000000000000000000000000000a":{"status":"pending"},"0x000000000000000000000000000000000000000000000000000000000000000f":{"status":"pending"}},"id":1}"#; assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); } diff --git a/rpc/src/v1/tests/mocked/parity_accounts.rs b/rpc/src/v1/tests/mocked/parity_accounts.rs index 258310b85a35ac2358f184f8b7a9298fe5e3431e..8342641d6cfe79b7bb09809dbe01f4475631039e 100644 --- a/rpc/src/v1/tests/mocked/parity_accounts.rs +++ b/rpc/src/v1/tests/mocked/parity_accounts.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -40,7 +40,7 @@ fn accounts_provider_with_vaults_support(temp_path: &str) -> Arc) -> ParityAccountsTester { - let opt_ap = Some(accounts_provider.clone()); + let opt_ap = accounts_provider.clone(); let parity_accounts = ParityAccountsClient::new(&opt_ap); let mut io = IoHandler::default(); io.extend_with(parity_accounts.to_delegate()); @@ -206,7 +206,6 @@ fn rpc_parity_set_and_get_new_dapps_default_address() { assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); } - #[test] fn rpc_parity_recent_dapps() { // given @@ -474,7 +473,6 @@ fn derive_key_index() { assert_eq!(res, Some(response.into())); } - #[test] fn should_export_account() { // given diff --git a/rpc/src/v1/tests/mocked/parity_set.rs b/rpc/src/v1/tests/mocked/parity_set.rs index f92841ee8b5f18307fb2bbf989ef9fca41d9c6d9..bc9f04de7cf5d5ba3b77c25b86181a6ad18fff36 100644 --- a/rpc/src/v1/tests/mocked/parity_set.rs +++ b/rpc/src/v1/tests/mocked/parity_set.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -21,14 +21,16 @@ use ethereum_types::{U256, Address}; use ethcore::miner::MinerService; use ethcore::client::TestBlockChainClient; -use ethsync::ManageNetwork; +use sync::ManageNetwork; use futures_cpupool::CpuPool; use jsonrpc_core::IoHandler; use v1::{ParitySet, ParitySetClient}; -use v1::tests::helpers::{TestMinerService, TestFetch, TestUpdater, TestDappsService}; +use v1::tests::helpers::{TestMinerService, TestUpdater, TestDappsService}; use super::manage_network::TestManageNetwork; +use fake_fetch::FakeFetch; + fn miner_service() -> Arc { Arc::new(TestMinerService::default()) } @@ -45,7 +47,7 @@ fn updater_service() -> Arc { Arc::new(TestUpdater::default()) } -pub type TestParitySetClient = ParitySetClient; +pub type TestParitySetClient = ParitySetClient>; fn parity_set_client( client: &Arc, @@ -55,7 +57,7 @@ fn parity_set_client( ) -> TestParitySetClient { let dapps_service = Arc::new(TestDappsService); let pool = CpuPool::new(1); - ParitySetClient::new(client, miner, updater, &(net.clone() as Arc), Some(dapps_service), TestFetch::default(), pool) + ParitySetClient::new(client, miner, updater, &(net.clone() as Arc), Some(dapps_service), FakeFetch::new(Some(1)), pool) } #[test] @@ -107,10 +109,9 @@ fn rpc_parity_set_min_gas_price() { io.extend_with(parity_set_client(&client, &miner, &updater, &network).to_delegate()); let request = r#"{"jsonrpc": "2.0", "method": "parity_setMinGasPrice", "params":["0xcd1722f3947def4cf144679da39c4c32bdc35681"], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":false,"id":1}"#; assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); - assert_eq!(miner.minimal_gas_price(), U256::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap()); } #[test] @@ -127,7 +128,7 @@ fn rpc_parity_set_gas_floor_target() { let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); - assert_eq!(miner.gas_floor_target(), U256::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap()); + assert_eq!(miner.authoring_params().gas_range_target.0, U256::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap()); } #[test] @@ -144,7 +145,7 @@ fn rpc_parity_set_extra_data() { let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); - assert_eq!(miner.extra_data(), "cd1722f3947def4cf144679da39c4c32bdc35681".from_hex().unwrap()); + assert_eq!(miner.authoring_params().extra_data, "cd1722f3947def4cf144679da39c4c32bdc35681".from_hex().unwrap()); } #[test] @@ -160,7 +161,7 @@ fn rpc_parity_set_author() { let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); - assert_eq!(miner.author(), Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap()); + assert_eq!(miner.authoring_params().author, Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap()); } #[test] @@ -176,11 +177,10 @@ fn rpc_parity_set_engine_signer() { let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); - assert_eq!(miner.author(), Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap()); + assert_eq!(miner.authoring_params().author, Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap()); assert_eq!(*miner.password.read(), "password".to_string()); } - #[test] fn rpc_parity_set_transactions_limit() { let miner = miner_service(); @@ -191,10 +191,9 @@ fn rpc_parity_set_transactions_limit() { io.extend_with(parity_set_client(&client, &miner, &updater, &network).to_delegate()); let request = r#"{"jsonrpc": "2.0", "method": "parity_setTransactionsLimit", "params":[10240240], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":false,"id":1}"#; assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); - assert_eq!(miner.transactions_limit(), 10_240_240); } #[test] @@ -234,7 +233,7 @@ fn rpc_parity_remove_transaction() { let signed = tx.fake_sign(2.into()); let hash = signed.hash(); - let request = r#"{"jsonrpc": "2.0", "method": "parity_removeTransaction", "params":[""#.to_owned() + &format!("0x{:?}", hash) + r#""], "id": 1}"#; + let request = r#"{"jsonrpc": "2.0", "method": "parity_removeTransaction", "params":[""#.to_owned() + &format!("0x{:x}", hash) + r#""], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"chainId":null,"condition":null,"creates":null,"from":"0x0000000000000000000000000000000000000002","gas":"0x76c0","gasPrice":"0x9184e72a000","hash":"0xa2e0da8a8064e0b9f93e95a53c2db6d01280efb8ac72a708d25487e67dd0f8fc","input":"0x","nonce":"0x1","publicKey":null,"r":"0x1","raw":"0xe9018609184e72a0008276c0940000000000000000000000000000000000000005849184e72a80800101","s":"0x1","standardV":"0x4","to":"0x0000000000000000000000000000000000000005","transactionIndex":null,"v":"0x0","value":"0x9184e72a"},"id":1}"#; miner.pending_transactions.lock().insert(hash, signed); diff --git a/rpc/src/v1/tests/mocked/personal.rs b/rpc/src/v1/tests/mocked/personal.rs index 6dd1da567fee080bdb15cd245500241ddd18c740..1e445c67a07dd648997003c8c69f03cb0d55580d 100644 --- a/rpc/src/v1/tests/mocked/personal.rs +++ b/rpc/src/v1/tests/mocked/personal.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -52,13 +52,12 @@ fn miner_service() -> Arc { fn setup() -> PersonalTester { let accounts = accounts_provider(); - let opt_accounts = Some(accounts.clone()); let client = blockchain_client(); let miner = miner_service(); let reservations = Arc::new(Mutex::new(nonce::Reservations::new())); let dispatcher = FullDispatcher::new(client, miner.clone(), reservations, 50); - let personal = PersonalClient::new(opt_accounts, dispatcher, false); + let personal = PersonalClient::new(&accounts, dispatcher, false); let mut io = IoHandler::default(); io.extend_with(personal.to_delegate()); @@ -77,7 +76,7 @@ fn accounts() { let tester = setup(); let address = tester.accounts.new_account("").unwrap(); let request = r#"{"jsonrpc": "2.0", "method": "personal_listAccounts", "params": [], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":[""#.to_owned() + &format!("0x{:?}", address) + r#""],"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":[""#.to_owned() + &format!("0x{:x}", address) + r#""],"id":1}"#; assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); } @@ -92,7 +91,7 @@ fn new_account() { let accounts = tester.accounts.accounts().unwrap(); assert_eq!(accounts.len(), 1); let address = accounts[0]; - let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"","id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:x}", address).as_ref() + r#"","id":1}"#; assert_eq!(res, Some(response)); } @@ -106,7 +105,7 @@ fn invalid_password_test(method: &str) "jsonrpc": "2.0", "method": ""#.to_owned() + method + r#"", "params": [{ - "from": ""# + format!("0x{:?}", address).as_ref() + r#"", + "from": ""# + format!("0x{:x}", address).as_ref() + r#"", "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", "gas": "0x76c0", "gasPrice": "0x9184e72a000", @@ -131,7 +130,7 @@ fn sign() { "method": "personal_sign", "params": [ ""#.to_owned() + format!("0x{}", data.to_hex()).as_ref() + r#"", - ""# + format!("0x{:?}", address).as_ref() + r#"", + ""# + format!("0x{:x}", address).as_ref() + r#"", "password123" ], "id": 1 @@ -156,7 +155,7 @@ fn sign_with_invalid_password() { "method": "personal_sign", "params": [ "0x0000000000000000000000000000000000000000000000000000000000000005", - ""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"", + ""#.to_owned() + format!("0x{:x}", address).as_ref() + r#"", "" ], "id": 1 @@ -195,7 +194,7 @@ fn sign_and_send_test(method: &str) { "jsonrpc": "2.0", "method": ""#.to_owned() + method + r#"", "params": [{ - "from": ""# + format!("0x{:?}", address).as_ref() + r#"", + "from": ""# + format!("0x{:x}", address).as_ref() + r#"", "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", "gas": "0x76c0", "gasPrice": "0x9184e72a000", @@ -216,11 +215,11 @@ fn sign_and_send_test(method: &str) { let signature = tester.accounts.sign(address, None, t.hash(None)).unwrap(); let t = t.with_signature(signature, None); - let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:?}", t.hash()).as_ref() + r#"","id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:x}", t.hash()).as_ref() + r#"","id":1}"#; assert_eq!(tester.io.handle_request_sync(request.as_ref()), Some(response)); - tester.miner.last_nonces.write().insert(address.clone(), U256::zero()); + tester.miner.increment_nonce(&address); let t = Transaction { nonce: U256::one(), @@ -234,7 +233,7 @@ fn sign_and_send_test(method: &str) { let signature = tester.accounts.sign(address, None, t.hash(None)).unwrap(); let t = t.with_signature(signature, None); - let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:?}", t.hash()).as_ref() + r#"","id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:x}", t.hash()).as_ref() + r#"","id":1}"#; assert_eq!(tester.io.handle_request_sync(request.as_ref()), Some(response)); } @@ -259,7 +258,7 @@ fn ec_recover() { "id": 1 }"#; - let address = format!("0x{:?}", address); + let address = format!("0x{:x}", address); let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + &address + r#"","id":1}"#; assert_eq!(tester.io.handle_request_sync(request.as_ref()), Some(response.into())); @@ -294,7 +293,7 @@ fn should_unlock_not_account_temporarily_if_allow_perm_is_disabled() { "jsonrpc": "2.0", "method": "personal_unlockAccount", "params": [ - ""#.to_owned() + &format!("0x{:?}", address) + r#"", + ""#.to_owned() + &format!("0x{:x}", address) + r#"", "password123", "0x100" ], @@ -315,7 +314,7 @@ fn should_unlock_account_permanently() { "jsonrpc": "2.0", "method": "personal_unlockAccount", "params": [ - ""#.to_owned() + &format!("0x{:?}", address) + r#"", + ""#.to_owned() + &format!("0x{:x}", address) + r#"", "password123", null ], diff --git a/rpc/src/v1/tests/mocked/pubsub.rs b/rpc/src/v1/tests/mocked/pubsub.rs index 99b34366c8f99ca72e8cdf76ad4559f7701dd5e5..a21f8a49033e6ded5f5f1d87f8731c68ad91a28f 100644 --- a/rpc/src/v1/tests/mocked/pubsub.rs +++ b/rpc/src/v1/tests/mocked/pubsub.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -75,4 +75,3 @@ fn should_subscribe_to_a_method() { let (res, _receiver) = receiver.into_future().wait().unwrap(); assert_eq!(res, None); } - diff --git a/rpc/src/v1/tests/mocked/rpc.rs b/rpc/src/v1/tests/mocked/rpc.rs index d0a6d2fab44150368128857a74ecc20944d8ea71..ed6503cea5fa100ca41323661331d8a1a15fa35d 100644 --- a/rpc/src/v1/tests/mocked/rpc.rs +++ b/rpc/src/v1/tests/mocked/rpc.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -18,7 +18,6 @@ use std::collections::BTreeMap; use jsonrpc_core::IoHandler; use v1::{Rpc, RpcClient}; - fn rpc_client() -> RpcClient { let mut modules = BTreeMap::new(); modules.insert("rpc".to_owned(), "1.0".to_owned()); diff --git a/rpc/src/v1/tests/mocked/secretstore.rs b/rpc/src/v1/tests/mocked/secretstore.rs index d3ab781bcfdb26d77d9a39e8d526bd3504ed1aca..33592a4883a89cc06fbdd11dd1a3326cfbc8d15c 100644 --- a/rpc/src/v1/tests/mocked/secretstore.rs +++ b/rpc/src/v1/tests/mocked/secretstore.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -40,7 +40,7 @@ impl Dependencies { } pub fn client(&self) -> SecretStoreClient { - SecretStoreClient::new(&Some(self.accounts.clone())) + SecretStoreClient::new(&self.accounts) } fn default_client(&self) -> IoHandler { diff --git a/rpc/src/v1/tests/mocked/signer.rs b/rpc/src/v1/tests/mocked/signer.rs index da531a00f153c74b286b518842c21706e4674046..b9358184224ff94b558e43f487a1b5e84672a808 100644 --- a/rpc/src/v1/tests/mocked/signer.rs +++ b/rpc/src/v1/tests/mocked/signer.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -58,7 +58,6 @@ fn miner_service() -> Arc { fn signer_tester() -> SignerTester { let signer = Arc::new(SignerService::new_test(false)); let accounts = accounts_provider(); - let opt_accounts = Some(accounts.clone()); let client = blockchain_client(); let miner = miner_service(); let reservations = Arc::new(Mutex::new(nonce::Reservations::new())); @@ -66,7 +65,7 @@ fn signer_tester() -> SignerTester { let dispatcher = FullDispatcher::new(client, miner.clone(), reservations, 50); let mut io = IoHandler::default(); - io.extend_with(SignerClient::new(&opt_accounts, dispatcher, &signer, event_loop.remote()).to_delegate()); + io.extend_with(SignerClient::new(&accounts, dispatcher, &signer, event_loop.remote()).to_delegate()); SignerTester { signer: signer, @@ -76,7 +75,6 @@ fn signer_tester() -> SignerTester { } } - #[test] fn should_return_list_of_items_to_confirm() { // given @@ -107,7 +105,6 @@ fn should_return_list_of_items_to_confirm() { assert_eq!(tester.io.handle_request_sync(&request), Some(response.to_owned())); } - #[test] fn should_reject_transaction_from_queue_without_dispatching() { // given @@ -216,7 +213,7 @@ fn should_confirm_transaction_and_dispatch() { "params":["0x1", {"gasPrice":"0x1000","gas":"0x50505"}, "test"], "id":1 }"#; - let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:?}", t.hash()).as_ref() + r#"","id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:x}", t.hash()).as_ref() + r#"","id":1}"#; // then assert_eq!(tester.io.handle_request_sync(&request), Some(response.to_owned())); @@ -261,11 +258,11 @@ fn should_alter_the_sender_and_nonce() { "jsonrpc":"2.0", "method":"signer_confirmRequest", "params":["0x1", {"sender":""#.to_owned() - + &format!("0x{:?}", address) + + &format!("0x{:x}", address) + r#"","gasPrice":"0x1000","gas":"0x50505"}, "test"], "id":1 }"#; - let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + &format!("0x{:?}", t.hash()) + r#"","id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + &format!("0x{:x}", t.hash()) + r#"","id":1}"#; // then assert_eq!(tester.io.handle_request_sync(&request), Some(response.to_owned())); @@ -312,7 +309,7 @@ fn should_confirm_transaction_with_token() { "id":1 }"#; let response = r#"{"jsonrpc":"2.0","result":{"result":""#.to_owned() + - format!("0x{:?}", t.hash()).as_ref() + + format!("0x{:x}", t.hash()).as_ref() + r#"","token":""#; // then @@ -361,7 +358,7 @@ fn should_confirm_transaction_with_rlp() { "params":["0x1", "0x"#.to_owned() + &rlp.to_hex() + r#""], "id":1 }"#; - let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:?}", t.hash()).as_ref() + r#"","id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:x}", t.hash()).as_ref() + r#"","id":1}"#; // then assert_eq!(tester.io.handle_request_sync(&request), Some(response.to_owned())); @@ -460,12 +457,12 @@ fn should_confirm_sign_transaction_with_rlp() { r#""blockHash":null,"blockNumber":null,"# + &format!("\"chainId\":{},", t.chain_id().map_or("null".to_owned(), |n| format!("{}", n))) + r#""condition":null,"creates":null,"# + - &format!("\"from\":\"0x{:?}\",", &address) + + &format!("\"from\":\"0x{:x}\",", &address) + r#""gas":"0x989680","gasPrice":"0x1000","# + - &format!("\"hash\":\"0x{:?}\",", t.hash()) + + &format!("\"hash\":\"0x{:x}\",", t.hash()) + r#""input":"0x","# + r#""nonce":"0x0","# + - &format!("\"publicKey\":\"0x{:?}\",", t.public_key().unwrap()) + + &format!("\"publicKey\":\"0x{:x}\",", t.public_key().unwrap()) + &format!("\"r\":\"0x{:x}\",", U256::from(signature.r())) + &format!("\"raw\":\"0x{}\",", rlp.to_hex()) + &format!("\"s\":\"0x{:x}\",", U256::from(signature.s())) + diff --git a/rpc/src/v1/tests/mocked/signing.rs b/rpc/src/v1/tests/mocked/signing.rs index 5bf5119ec6008d6f869eb3901310d7a79996c813..42d20bbf83d7c8de344d329f04572241c51db37c 100644 --- a/rpc/src/v1/tests/mocked/signing.rs +++ b/rpc/src/v1/tests/mocked/signing.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -56,7 +56,6 @@ impl Default for SigningTester { let client = Arc::new(TestBlockChainClient::default()); let miner = Arc::new(TestMinerService::default()); let accounts = Arc::new(AccountProvider::transient_provider()); - let opt_accounts = Some(accounts.clone()); let reservations = Arc::new(Mutex::new(nonce::Reservations::new())); let mut io = IoHandler::default(); @@ -64,9 +63,9 @@ impl Default for SigningTester { let remote = Remote::new_thread_per_future(); - let rpc = SigningQueueClient::new(&signer, dispatcher.clone(), remote.clone(), &opt_accounts); + let rpc = SigningQueueClient::new(&signer, dispatcher.clone(), remote.clone(), &accounts); io.extend_with(EthSigning::to_delegate(rpc)); - let rpc = SigningQueueClient::new(&signer, dispatcher, remote, &opt_accounts); + let rpc = SigningQueueClient::new(&signer, dispatcher, remote, &accounts); io.extend_with(ParitySigning::to_delegate(rpc)); SigningTester { @@ -95,7 +94,7 @@ fn should_add_sign_to_queue() { "jsonrpc": "2.0", "method": "eth_sign", "params": [ - ""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"", + ""#.to_owned() + format!("0x{:x}", address).as_ref() + r#"", "0x0000000000000000000000000000000000000000000000000000000000000005" ], "id": 1 @@ -110,7 +109,8 @@ fn should_add_sign_to_queue() { ::std::thread::spawn(move || loop { if signer.requests().len() == 1 { // respond - signer.request_confirmed(1.into(), Ok(ConfirmationResponse::Signature(0.into()))); + let sender = signer.take(&1.into()).unwrap(); + signer.request_confirmed(sender, Ok(ConfirmationResponse::Signature(0.into()))); break } ::std::thread::sleep(Duration::from_millis(100)) @@ -132,7 +132,7 @@ fn should_post_sign_to_queue() { "jsonrpc": "2.0", "method": "parity_postSign", "params": [ - ""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"", + ""#.to_owned() + format!("0x{:x}", address).as_ref() + r#"", "0x0000000000000000000000000000000000000000000000000000000000000005" ], "id": 1 @@ -153,7 +153,7 @@ fn should_check_status_of_request() { "jsonrpc": "2.0", "method": "parity_postSign", "params": [ - ""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"", + ""#.to_owned() + format!("0x{:x}", address).as_ref() + r#"", "0x0000000000000000000000000000000000000000000000000000000000000005" ], "id": 1 @@ -182,13 +182,14 @@ fn should_check_status_of_request_when_its_resolved() { "jsonrpc": "2.0", "method": "parity_postSign", "params": [ - ""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"", + ""#.to_owned() + format!("0x{:x}", address).as_ref() + r#"", "0x0000000000000000000000000000000000000000000000000000000000000005" ], "id": 1 }"#; tester.io.handle_request_sync(&request).expect("Sent"); - tester.signer.request_confirmed(1.into(), Ok(ConfirmationResponse::Signature(1.into()))); + let sender = tester.signer.take(&1.into()).unwrap(); + tester.signer.request_confirmed(sender, Ok(ConfirmationResponse::Signature(1.into()))); // This is not ideal, but we need to give futures some time to be executed, and they need to run in a separate thread thread::sleep(Duration::from_millis(20)); @@ -211,7 +212,7 @@ fn should_sign_if_account_is_unlocked() { // given let tester = eth_signing(); let data = vec![5u8]; - let acc = tester.accounts.insert_account(Secret::from_slice(&[69u8; 32]), "test").unwrap(); + let acc = tester.accounts.insert_account(Secret::from([69u8; 32]), "test").unwrap(); tester.accounts.unlock_account_permanently(acc, "test".into()).unwrap(); // when @@ -219,7 +220,7 @@ fn should_sign_if_account_is_unlocked() { "jsonrpc": "2.0", "method": "eth_sign", "params": [ - ""#.to_owned() + format!("0x{:?}", acc).as_ref() + r#"", + ""#.to_owned() + format!("0x{:x}", acc).as_ref() + r#"", ""# + format!("0x{}", data.to_hex()).as_ref() + r#"" ], "id": 1 @@ -241,7 +242,7 @@ fn should_add_transaction_to_queue() { "jsonrpc": "2.0", "method": "eth_sendTransaction", "params": [{ - "from": ""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"", + "from": ""#.to_owned() + format!("0x{:x}", address).as_ref() + r#"", "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", "gas": "0x76c0", "gasPrice": "0x9184e72a000", @@ -259,7 +260,8 @@ fn should_add_transaction_to_queue() { ::std::thread::spawn(move || loop { if signer.requests().len() == 1 { // respond - signer.request_confirmed(1.into(), Ok(ConfirmationResponse::SendTransaction(0.into()))); + let sender = signer.take(&1.into()).unwrap(); + signer.request_confirmed(sender, Ok(ConfirmationResponse::SendTransaction(0.into()))); break } ::std::thread::sleep(Duration::from_millis(100)) @@ -282,7 +284,7 @@ fn should_add_sign_transaction_to_the_queue() { "jsonrpc": "2.0", "method": "eth_signTransaction", "params": [{ - "from": ""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"", + "from": ""#.to_owned() + format!("0x{:x}", address).as_ref() + r#"", "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", "gas": "0x76c0", "gasPrice": "0x9184e72a000", @@ -311,12 +313,12 @@ fn should_add_sign_transaction_to_the_queue() { r#""blockHash":null,"blockNumber":null,"# + &format!("\"chainId\":{},", t.chain_id().map_or("null".to_owned(), |n| format!("{}", n))) + r#""condition":null,"creates":null,"# + - &format!("\"from\":\"0x{:?}\",", &address) + + &format!("\"from\":\"0x{:x}\",", &address) + r#""gas":"0x76c0","gasPrice":"0x9184e72a000","# + - &format!("\"hash\":\"0x{:?}\",", t.hash()) + + &format!("\"hash\":\"0x{:x}\",", t.hash()) + r#""input":"0x","# + r#""nonce":"0x1","# + - &format!("\"publicKey\":\"0x{:?}\",", t.public_key().unwrap()) + + &format!("\"publicKey\":\"0x{:x}\",", t.public_key().unwrap()) + &format!("\"r\":\"0x{:x}\",", U256::from(signature.r())) + &format!("\"raw\":\"0x{}\",", rlp.to_hex()) + &format!("\"s\":\"0x{:x}\",", U256::from(signature.s())) + @@ -327,7 +329,7 @@ fn should_add_sign_transaction_to_the_queue() { r#"}},"id":1}"#; // then - tester.miner.last_nonces.write().insert(address.clone(), U256::zero()); + tester.miner.increment_nonce(&address); let promise = tester.io.handle_request(&request); // the future must be polled at least once before request is queued. @@ -335,7 +337,8 @@ fn should_add_sign_transaction_to_the_queue() { ::std::thread::spawn(move || loop { if signer.requests().len() == 1 { // respond - signer.request_confirmed(1.into(), Ok(ConfirmationResponse::SignTransaction( + let sender = signer.take(&1.into()).unwrap(); + signer.request_confirmed(sender, Ok(ConfirmationResponse::SignTransaction( RichRawTransaction::from_signed(t.into(), 0x0, u64::max_value()) ))); break @@ -370,7 +373,7 @@ fn should_dispatch_transaction_if_account_is_unlock() { "jsonrpc": "2.0", "method": "eth_sendTransaction", "params": [{ - "from": ""#.to_owned() + format!("0x{:?}", acc).as_ref() + r#"", + "from": ""#.to_owned() + format!("0x{:x}", acc).as_ref() + r#"", "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", "gas": "0x76c0", "gasPrice": "0x9184e72a000", @@ -378,7 +381,7 @@ fn should_dispatch_transaction_if_account_is_unlock() { }], "id": 1 }"#; - let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:?}", t.hash()).as_ref() + r#"","id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:x}", t.hash()).as_ref() + r#"","id":1}"#; // then assert_eq!(tester.io.handle_request_sync(&request), Some(response.to_owned())); @@ -393,9 +396,8 @@ fn should_decrypt_message_if_account_is_unlocked() { let (address, public) = tester.accounts.new_account_and_public("test").unwrap(); tester.accounts.unlock_account_permanently(address, "test".into()).unwrap(); - // First encrypt message - let request = format!("{}0x{:?}{}", + let request = format!("{}0x{:x}{}", r#"{"jsonrpc": "2.0", "method": "parity_encryptMessage", "params":[""#, public, r#"", "0x01020304"], "id": 1}"# @@ -403,7 +405,7 @@ fn should_decrypt_message_if_account_is_unlocked() { let encrypted: Success = serde_json::from_str(&tester.io.handle_request_sync(&request).unwrap()).unwrap(); // then call decrypt - let request = format!("{}{:?}{}{}{}", + let request = format!("{}{:x}{}{}{}", r#"{"jsonrpc": "2.0", "method": "parity_decryptMessage", "params":["0x"#, address, r#"","#, @@ -428,7 +430,7 @@ fn should_add_decryption_to_the_queue() { let request = r#"{ "jsonrpc": "2.0", "method": "parity_decryptMessage", - "params": ["0x"#.to_owned() + &format!("{:?}", acc.address()) + r#"", + "params": ["0x"#.to_owned() + &format!("{:x}", acc.address()) + r#"", "0x012345"], "id": 1 }"#; @@ -442,7 +444,8 @@ fn should_add_decryption_to_the_queue() { ::std::thread::spawn(move || loop { if signer.requests().len() == 1 { // respond - signer.request_confirmed(1.into(), Ok(ConfirmationResponse::Decrypt(vec![0x1, 0x2].into()))); + let sender = signer.take(&1.into()).unwrap(); + signer.request_confirmed(sender, Ok(ConfirmationResponse::Decrypt(vec![0x1, 0x2].into()))); break } ::std::thread::sleep(Duration::from_millis(10)) @@ -459,7 +462,7 @@ fn should_compose_transaction() { let tester = eth_signing(); let acc = Random.generate().unwrap(); assert_eq!(tester.signer.requests().len(), 0); - let from = format!("{:?}", acc.address()); + let from = format!("{:x}", acc.address()); // when let request = r#"{ @@ -473,7 +476,6 @@ fn should_compose_transaction() { + &from + r#"","gas":"0x5208","gasPrice":"0x4a817c800","nonce":"0x0","to":null,"value":"0x5"},"id":1}"#; - // then let res = tester.io.handle_request(&request).wait().unwrap(); assert_eq!(res, Some(response.to_owned())); diff --git a/rpc/src/v1/tests/mocked/traces.rs b/rpc/src/v1/tests/mocked/traces.rs index 0b2e7d5ccb84ead1dd078937b8658d0a8da1f7bb..70a862d332a2a22591b74877b653790e585cd802 100644 --- a/rpc/src/v1/tests/mocked/traces.rs +++ b/rpc/src/v1/tests/mocked/traces.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/tests/mocked/web3.rs b/rpc/src/v1/tests/mocked/web3.rs index aceb36e5651a1ed264db86cd9b6ede846c257e48..e16c5f4926fd086e3291515a4a3d3486a485d7da 100644 --- a/rpc/src/v1/tests/mocked/web3.rs +++ b/rpc/src/v1/tests/mocked/web3.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -24,7 +24,7 @@ fn rpc_web3_version() { let mut io = IoHandler::new(); io.extend_with(web3); - let v = version().to_owned().replace("Parity/", "Parity//"); + let v = version().to_owned().replacen("/", "//", 1); let request = r#"{"jsonrpc": "2.0", "method": "web3_clientVersion", "params": [], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","result":"VER","id":1}"#.to_owned().replace("VER", v.as_ref()); diff --git a/rpc/src/v1/tests/mod.rs b/rpc/src/v1/tests/mod.rs index 31ac1c5410cb6a1275165ac755c54dc8226abe4a..471569e52319155f1c807dee81e4160cbc29d650 100644 --- a/rpc/src/v1/tests/mod.rs +++ b/rpc/src/v1/tests/mod.rs @@ -1,3 +1,19 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + //! RPC unit test moduleS pub mod helpers; diff --git a/rpc/src/v1/traits/eth.rs b/rpc/src/v1/traits/eth.rs index 111739e4ab94ebe647645e88f2fff0c4e99a0ff6..56f6527579276be4a69e4c101ad6b9df312e188e 100644 --- a/rpc/src/v1/traits/eth.rs +++ b/rpc/src/v1/traits/eth.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/traits/eth_pubsub.rs b/rpc/src/v1/traits/eth_pubsub.rs index cfbe4c54bcb617006d99b532593b7347ce7bf053..38babeef42c85c9bbe686920346ae79fdeab7e2f 100644 --- a/rpc/src/v1/traits/eth_pubsub.rs +++ b/rpc/src/v1/traits/eth_pubsub.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/traits/eth_signing.rs b/rpc/src/v1/traits/eth_signing.rs index 9830ac54d8272c100867fc64e381adb56dbc9a41..27657475bab4b6e60e913e41e88988acca1f3449 100644 --- a/rpc/src/v1/traits/eth_signing.rs +++ b/rpc/src/v1/traits/eth_signing.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/traits/mod.rs b/rpc/src/v1/traits/mod.rs index 528463a4a4e8640cb6f176b6812cd2452da83f16..62edac8ed666ba55ad0abdbf6ea91dd9b2d3f734 100644 --- a/rpc/src/v1/traits/mod.rs +++ b/rpc/src/v1/traits/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -31,6 +31,7 @@ pub mod signer; pub mod traces; pub mod rpc; pub mod secretstore; +pub mod private; pub use self::web3::Web3; pub use self::eth::{Eth, EthFilter}; @@ -47,3 +48,4 @@ pub use self::signer::Signer; pub use self::traces::Traces; pub use self::rpc::Rpc; pub use self::secretstore::SecretStore; +pub use self::private::Private; diff --git a/rpc/src/v1/traits/net.rs b/rpc/src/v1/traits/net.rs index bc2068ff9ab7eaf5d6505a13702ce3db1e66fe91..d70a4653a6e01ba10487b03f796fa331fa531803 100644 --- a/rpc/src/v1/traits/net.rs +++ b/rpc/src/v1/traits/net.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/traits/parity.rs b/rpc/src/v1/traits/parity.rs index 81c406fb3a3fcd7c99ef08388a7ecc512e492e99..1b9a7d09f5bb0f671e792da5bc273cec25505f4d 100644 --- a/rpc/src/v1/traits/parity.rs +++ b/rpc/src/v1/traits/parity.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -141,9 +141,15 @@ build_rpc_trait! { /// Returns all pending transactions from transaction queue. #[rpc(name = "parity_pendingTransactions")] - fn pending_transactions(&self) -> Result>; + fn pending_transactions(&self, Trailing) -> Result>; - /// Returns all future transactions from transaction queue. + /// Returns all transactions from transaction queue. + /// + /// Some of them might not be ready to be included in a block yet. + #[rpc(name = "parity_allTransactions")] + fn all_transactions(&self) -> Result>; + + /// Returns all future transactions from transaction queue (deprecated) #[rpc(name = "parity_futureTransactions")] fn future_transactions(&self) -> Result>; @@ -172,7 +178,7 @@ build_rpc_trait! { fn mode(&self) -> Result; /// Returns the chain ID used for transaction signing at the - /// current best block. An empty string is returned if not + /// current best block. None is returned if not /// available. #[rpc(name = "parity_chainId")] fn chain_id(&self) -> Result>; diff --git a/rpc/src/v1/traits/parity_accounts.rs b/rpc/src/v1/traits/parity_accounts.rs index 494f1576cbd903b0902c5b0ae2ac076ec10bb79c..977593d44eda380c22c3ae7dd38231a7392782c8 100644 --- a/rpc/src/v1/traits/parity_accounts.rs +++ b/rpc/src/v1/traits/parity_accounts.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/traits/parity_set.rs b/rpc/src/v1/traits/parity_set.rs index 40aad1a4bd909b16bbbc2d56a430be74026b4e6b..8cfffb50c7ce49be2ed5cdd5c74e7bca99d50e15 100644 --- a/rpc/src/v1/traits/parity_set.rs +++ b/rpc/src/v1/traits/parity_set.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/traits/parity_signing.rs b/rpc/src/v1/traits/parity_signing.rs index 8015b04317bb755d6fb5933ee9a1e7951ace48bf..208422222ec538eb522677961d15ba1a6a38b9f2 100644 --- a/rpc/src/v1/traits/parity_signing.rs +++ b/rpc/src/v1/traits/parity_signing.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/traits/personal.rs b/rpc/src/v1/traits/personal.rs index 7c187fcffe9bd22954a36f24fd5c443375a40a22..7187219105aa6801b4f681dfe4c2bb60fa878b89 100644 --- a/rpc/src/v1/traits/personal.rs +++ b/rpc/src/v1/traits/personal.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/traits/private.rs b/rpc/src/v1/traits/private.rs new file mode 100644 index 0000000000000000000000000000000000000000..b7b1aa20a78297a764ec2f6135c133f03a52a825 --- /dev/null +++ b/rpc/src/v1/traits/private.rs @@ -0,0 +1,45 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! SecretStore-specific rpc interface. + +use jsonrpc_core::Error; + +use v1::types::{Bytes, PrivateTransactionReceipt, H160, H256, U256, BlockNumber, + PrivateTransactionReceiptAndTransaction, CallRequest}; + +build_rpc_trait! { + /// Private transaction management RPC interface. + pub trait Private { + type Metadata; + + /// Sends private transaction; Transaction will be added to the validation queue and sent out when ready. + #[rpc(name = "private_sendTransaction")] + fn send_transaction(&self, Bytes) -> Result; + + /// Creates a transaction for contract's deployment from origin (signed transaction) + #[rpc(name = "private_composeDeploymentTransaction")] + fn compose_deployment_transaction(&self, BlockNumber, Bytes, Vec, U256) -> Result; + + /// Make a call to the private contract + #[rpc(meta, name = "private_call")] + fn private_call(&self, Self::Metadata, BlockNumber, CallRequest) -> Result; + + /// Retrieve the id of the key associated with the contract + #[rpc(name = "private_contractKey")] + fn private_contract_key(&self, H160) -> Result; + } +} diff --git a/rpc/src/v1/traits/pubsub.rs b/rpc/src/v1/traits/pubsub.rs index 0b77fc64d6e309ca178e8bf0f0ce25caad47ae80..840de8d4b0fd3e5ad9cef25cb67068eeb6baf0e5 100644 --- a/rpc/src/v1/traits/pubsub.rs +++ b/rpc/src/v1/traits/pubsub.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/traits/rpc.rs b/rpc/src/v1/traits/rpc.rs index a813aa94e67c4dcc6b41984a4e2d728676e00bf6..8c0b3c2c90520e4580982c51598a39bc447f0343 100644 --- a/rpc/src/v1/traits/rpc.rs +++ b/rpc/src/v1/traits/rpc.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/traits/secretstore.rs b/rpc/src/v1/traits/secretstore.rs index 6d2e5669c007ac4f2f88ea3202608c1d5671a215..e15d71a72feea9f364bb143788864880207974c2 100644 --- a/rpc/src/v1/traits/secretstore.rs +++ b/rpc/src/v1/traits/secretstore.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/traits/signer.rs b/rpc/src/v1/traits/signer.rs index b7f60619e805619220fcc083c552beb1368dd5df..4ede0ce534a99c677ced8f0ef304d6f1258b56c7 100644 --- a/rpc/src/v1/traits/signer.rs +++ b/rpc/src/v1/traits/signer.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/traits/traces.rs b/rpc/src/v1/traits/traces.rs index 1fe01f47ef66869ea0ffc65ce1101304bffa8e95..2d3665f6bc19e59eea4134db127d4ea834c72528 100644 --- a/rpc/src/v1/traits/traces.rs +++ b/rpc/src/v1/traits/traces.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/traits/web3.rs b/rpc/src/v1/traits/web3.rs index e4fb8b0d1fbe6b0ce8c1312b2d87d8029a456929..713cd9a32d3ffd38d7a0293f4c91c5c4bc3c598f 100644 --- a/rpc/src/v1/traits/web3.rs +++ b/rpc/src/v1/traits/web3.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/types/account_info.rs b/rpc/src/v1/types/account_info.rs index cd239928a9a0eacb95fb5a22193133293dfbe189..8e12b54a6899becf79213ad1aa1f48456a238763 100644 --- a/rpc/src/v1/types/account_info.rs +++ b/rpc/src/v1/types/account_info.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -72,4 +72,3 @@ pub struct HwAccountInfo { /// Device manufacturer. pub manufacturer: String, } - diff --git a/rpc/src/v1/types/block.rs b/rpc/src/v1/types/block.rs index cd2d83e0e9f70a2af55e8cff1adb6c49ac9cede3..9ae870dc5584af1dade04f221d91be676a01a1bd 100644 --- a/rpc/src/v1/types/block.rs +++ b/rpc/src/v1/types/block.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -44,52 +44,42 @@ impl Serialize for BlockTransactions { /// Block representation #[derive(Debug, Serialize)] +#[serde(rename_all="camelCase")] pub struct Block { /// Hash of the block pub hash: Option, /// Hash of the parent - #[serde(rename="parentHash")] pub parent_hash: H256, /// Hash of the uncles #[serde(rename="sha3Uncles")] pub uncles_hash: H256, /// Authors address pub author: H160, - // TODO: get rid of this one - /// ? + /// Alias of `author` pub miner: H160, /// State root hash - #[serde(rename="stateRoot")] pub state_root: H256, /// Transactions root hash - #[serde(rename="transactionsRoot")] pub transactions_root: H256, /// Transactions receipts root hash - #[serde(rename="receiptsRoot")] pub receipts_root: H256, /// Block number pub number: Option, /// Gas Used - #[serde(rename="gasUsed")] pub gas_used: U256, /// Gas Limit - #[serde(rename="gasLimit")] pub gas_limit: U256, /// Extra data - #[serde(rename="extraData")] pub extra_data: Bytes, /// Logs bloom - #[serde(rename="logsBloom")] - pub logs_bloom: H2048, + pub logs_bloom: Option, /// Timestamp pub timestamp: U256, /// Difficulty pub difficulty: U256, /// Total difficulty - #[serde(rename="totalDifficulty")] pub total_difficulty: Option, /// Seal fields - #[serde(rename="sealFields")] pub seal_fields: Vec, /// Uncles' hashes pub uncles: Vec, @@ -101,49 +91,40 @@ pub struct Block { /// Block header representation. #[derive(Debug, Clone, Serialize, PartialEq, Eq)] +#[serde(rename_all="camelCase")] pub struct Header { /// Hash of the block pub hash: Option, /// Hash of the parent - #[serde(rename="parentHash")] pub parent_hash: H256, /// Hash of the uncles #[serde(rename="sha3Uncles")] pub uncles_hash: H256, /// Authors address pub author: H160, - // TODO: get rid of this one - /// ? + /// Alias of `author` pub miner: H160, /// State root hash - #[serde(rename="stateRoot")] pub state_root: H256, /// Transactions root hash - #[serde(rename="transactionsRoot")] pub transactions_root: H256, /// Transactions receipts root hash - #[serde(rename="receiptsRoot")] pub receipts_root: H256, /// Block number pub number: Option, /// Gas Used - #[serde(rename="gasUsed")] pub gas_used: U256, /// Gas Limit - #[serde(rename="gasLimit")] pub gas_limit: U256, /// Extra data - #[serde(rename="extraData")] pub extra_data: Bytes, /// Logs bloom - #[serde(rename="logsBloom")] pub logs_bloom: H2048, /// Timestamp pub timestamp: U256, /// Difficulty pub difficulty: U256, /// Seal fields - #[serde(rename="sealFields")] pub seal_fields: Vec, /// Size in bytes pub size: Option, @@ -254,7 +235,7 @@ mod tests { gas_used: U256::default(), gas_limit: U256::default(), extra_data: Bytes::default(), - logs_bloom: H2048::default(), + logs_bloom: Some(H2048::default()), timestamp: U256::default(), difficulty: U256::default(), total_difficulty: Some(U256::default()), @@ -292,7 +273,7 @@ mod tests { gas_used: U256::default(), gas_limit: U256::default(), extra_data: Bytes::default(), - logs_bloom: H2048::default(), + logs_bloom: Some(H2048::default()), timestamp: U256::default(), difficulty: U256::default(), total_difficulty: Some(U256::default()), diff --git a/rpc/src/v1/types/block_number.rs b/rpc/src/v1/types/block_number.rs index b6c1860f5ba4202c4048f3d7163ee8f76eb791d6..b92a0d4a3fe0f3d142a08b436e8bb9987dc09fdc 100644 --- a/rpc/src/v1/types/block_number.rs +++ b/rpc/src/v1/types/block_number.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -135,4 +135,3 @@ mod tests { block_number_to_id(BlockNumber::Pending); } } - diff --git a/rpc/src/v1/types/bytes.rs b/rpc/src/v1/types/bytes.rs index fdbcb729bbf615b604560b1800cb92ce23a4feb8..0bd62c601c96ab9c40853287b36ec734fbaaf910 100644 --- a/rpc/src/v1/types/bytes.rs +++ b/rpc/src/v1/types/bytes.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -86,7 +86,6 @@ impl<'a> Visitor<'a> for BytesVisitor { } } - #[cfg(test)] mod tests { use super::*; @@ -118,4 +117,3 @@ mod tests { assert_eq!(bytes6, Bytes(vec![0x1, 0x23])); } } - diff --git a/rpc/src/v1/types/call_request.rs b/rpc/src/v1/types/call_request.rs index 71b562e45a71036af6901f2177455555390d0ebd..39d4d17b785dd540bf36aeeba00cc3c2c96ff166 100644 --- a/rpc/src/v1/types/call_request.rs +++ b/rpc/src/v1/types/call_request.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/types/confirmations.rs b/rpc/src/v1/types/confirmations.rs index 5dcb11316c2c5d4bda9aa33b213c830596c4bfb8..7f4f3ad106e724671bb36f75112bb21bddc41ad5 100644 --- a/rpc/src/v1/types/confirmations.rs +++ b/rpc/src/v1/types/confirmations.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/types/consensus_status.rs b/rpc/src/v1/types/consensus_status.rs index 96657adbc9185ba8da97fa3a2949ddf80b80359d..0cbdf1f007864be6f3d83ba425e6fee23bc9324b 100644 --- a/rpc/src/v1/types/consensus_status.rs +++ b/rpc/src/v1/types/consensus_status.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/types/dapps.rs b/rpc/src/v1/types/dapps.rs index 418717fccfb9735b47d97aa7a6e3cfb72b3d0e9c..81339fb1dc2948899916ae65ceaae77155a121bb 100644 --- a/rpc/src/v1/types/dapps.rs +++ b/rpc/src/v1/types/dapps.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/types/derivation.rs b/rpc/src/v1/types/derivation.rs index 76becbaebe041cf6d4b7f66c9385489c3dcf0f71..0e39b65322f80c11d11218107ef81d78a5ecd839 100644 --- a/rpc/src/v1/types/derivation.rs +++ b/rpc/src/v1/types/derivation.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/types/filter.rs b/rpc/src/v1/types/filter.rs index 52217459c1f81ccdb6eff58b39177476fa41a5d7..dd8b823e879bcb823affc702f31e88f7f7e25b82 100644 --- a/rpc/src/v1/types/filter.rs +++ b/rpc/src/v1/types/filter.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/types/hash.rs b/rpc/src/v1/types/hash.rs index e3cc73e2720dbd3a3d0f11772022434f953a6c42..07c7ef24f45178353858c4149f79ad5aae8f34aa 100644 --- a/rpc/src/v1/types/hash.rs +++ b/rpc/src/v1/types/hash.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/types/histogram.rs b/rpc/src/v1/types/histogram.rs index 26bbc7d2de4272e8c15d75da3d7f079f302e2110..2b71b88bf6f5c40a28d5d1259d84f5c7c1b8aff1 100644 --- a/rpc/src/v1/types/histogram.rs +++ b/rpc/src/v1/types/histogram.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -12,7 +12,7 @@ // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity. If not, see . +// along with Parity. If not, see . //! Gas prices histogram. diff --git a/rpc/src/v1/types/index.rs b/rpc/src/v1/types/index.rs index 4e44ce49cea2c0a4e9c1d8a39e3b7a3f3fa2b826..4c8af6000451f34cf3353a1f377ba8da1a0370cc 100644 --- a/rpc/src/v1/types/index.rs +++ b/rpc/src/v1/types/index.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -73,4 +73,3 @@ mod tests { assert_eq!(deserialized, vec![Index(10), Index(10)]); } } - diff --git a/rpc/src/v1/types/log.rs b/rpc/src/v1/types/log.rs index e178516d6d11f1c02b79abc55848c6911861fe18..950b649d7d0b3cdb3205d27175dcfc444fe7f4f5 100644 --- a/rpc/src/v1/types/log.rs +++ b/rpc/src/v1/types/log.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -47,6 +47,9 @@ pub struct Log { /// Log Type #[serde(rename="type")] pub log_type: String, + /// Whether Log Type is Removed (Geth Compatibility Field) + #[serde(default)] + pub removed: bool, } impl From for Log { @@ -62,6 +65,7 @@ impl From for Log { log_index: Some(e.log_index.into()), transaction_log_index: Some(e.transaction_log_index.into()), log_type: "mined".to_owned(), + removed: false, } } } @@ -79,6 +83,7 @@ impl From for Log { log_index: None, transaction_log_index: None, log_type: "pending".to_owned(), + removed: false, } } } @@ -91,7 +96,7 @@ mod tests { #[test] fn log_serialization() { - let s = r#"{"address":"0x33990122638b9132ca29c723bdf037f1a891a70c","topics":["0xa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc","0x4861736852656700000000000000000000000000000000000000000000000000"],"data":"0x","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x4510c","transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","logIndex":"0x1","transactionLogIndex":"0x1","type":"mined"}"#; + let s = r#"{"address":"0x33990122638b9132ca29c723bdf037f1a891a70c","topics":["0xa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc","0x4861736852656700000000000000000000000000000000000000000000000000"],"data":"0x","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x4510c","transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","logIndex":"0x1","transactionLogIndex":"0x1","type":"mined","removed":false}"#; let log = Log { address: H160::from_str("33990122638b9132ca29c723bdf037f1a891a70c").unwrap(), @@ -107,6 +112,7 @@ mod tests { transaction_log_index: Some(1.into()), log_index: Some(U256::from(1)), log_type: "mined".to_owned(), + removed: false, }; let serialized = serde_json::to_string(&log).unwrap(); diff --git a/rpc/src/v1/types/mod.rs b/rpc/src/v1/types/mod.rs index b45c22821315d768d1db49eeb4ec2432b6420e8b..d2ec479fb5f295877f5c984fc73e550cef6e9dd4 100644 --- a/rpc/src/v1/types/mod.rs +++ b/rpc/src/v1/types/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -14,7 +14,6 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . - //! RPC types mod account_info; @@ -44,6 +43,7 @@ mod transaction_request; mod transaction_condition; mod uint; mod work; +mod private_receipt; pub mod pubsub; @@ -80,6 +80,7 @@ pub use self::transaction_request::TransactionRequest; pub use self::transaction_condition::TransactionCondition; pub use self::uint::{U128, U256, U64}; pub use self::work::Work; +pub use self::private_receipt::{PrivateTransactionReceipt, PrivateTransactionReceiptAndTransaction}; // TODO [ToDr] Refactor to a proper type Vec of enums? /// Expected tracing type. diff --git a/rpc/src/v1/types/node_kind.rs b/rpc/src/v1/types/node_kind.rs index 5c96fafc6306f19d3b4eb95f861712d4c0746639..8061d82808f28eae5031f8fe50657a463427717c 100644 --- a/rpc/src/v1/types/node_kind.rs +++ b/rpc/src/v1/types/node_kind.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/types/private_receipt.rs b/rpc/src/v1/types/private_receipt.rs new file mode 100644 index 0000000000000000000000000000000000000000..7e758af3a5f5d7334b5aa1160ad23103745581c2 --- /dev/null +++ b/rpc/src/v1/types/private_receipt.rs @@ -0,0 +1,53 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use v1::types::{H160, H256, TransactionRequest}; +use ethcore_private_tx::{Receipt as EthPrivateReceipt}; + +/// Receipt +#[derive(Debug, Serialize)] +pub struct PrivateTransactionReceipt { + /// Transaction Hash + #[serde(rename="transactionHash")] + pub transaction_hash: H256, + /// Private contract address + #[serde(rename="contractAddress")] + pub contract_address: Option, + /// Status code + #[serde(rename="status")] + pub status_code: u8, +} + +impl From for PrivateTransactionReceipt { + fn from(r: EthPrivateReceipt) -> Self { + PrivateTransactionReceipt { + transaction_hash: r.hash.into(), + contract_address: r.contract_address.map(Into::into), + status_code: r.status_code.into(), + } + } +} + +/// Receipt and Transaction +#[derive(Debug, Serialize)] +pub struct PrivateTransactionReceiptAndTransaction { + /// Receipt + #[serde(rename="receipt")] + pub receipt: PrivateTransactionReceipt, + /// Transaction + #[serde(rename="transaction")] + pub transaction: TransactionRequest, +} diff --git a/rpc/src/v1/types/provenance.rs b/rpc/src/v1/types/provenance.rs index b52f0cb7746f5822d29756aa08c8ebec7344b773..328f2ded3e6e1d21c27be857379282e9e4af710c 100644 --- a/rpc/src/v1/types/provenance.rs +++ b/rpc/src/v1/types/provenance.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -49,6 +49,9 @@ pub enum Origin { /// Session id session: H256 }, + /// From the C API + #[serde(rename="c-api")] + CApi, /// Unknown #[serde(rename="unknown")] Unknown, @@ -68,6 +71,7 @@ impl fmt::Display for Origin { Origin::Ipc(ref session) => write!(f, "IPC (session: {})", session), Origin::Ws { ref session, ref dapp } => write!(f, "{} via WebSocket (session: {})", dapp, session), Origin::Signer { ref session, ref dapp } => write!(f, "{} via UI (session: {})", dapp, session), + Origin::CApi => write!(f, "C API"), Origin::Unknown => write!(f, "unknown origin"), } } diff --git a/rpc/src/v1/types/pubsub.rs b/rpc/src/v1/types/pubsub.rs index dfac5a0abb561a333a094bce7ea3b859a39af5ca..ea01d6427bd2941ee2df57ebab1246fe0b09877c 100644 --- a/rpc/src/v1/types/pubsub.rs +++ b/rpc/src/v1/types/pubsub.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/types/receipt.rs b/rpc/src/v1/types/receipt.rs index e20856b8225993dd027a5a1ed80fa6c8d55503a3..9688f7d371634419df07430d11018905db9d2a39 100644 --- a/rpc/src/v1/types/receipt.rs +++ b/rpc/src/v1/types/receipt.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -29,6 +29,10 @@ pub struct Receipt { /// Block hash #[serde(rename="blockHash")] pub block_hash: Option, + /// Sender + pub from: Option, + /// Recipient + pub to: Option, /// Block number #[serde(rename="blockNumber")] pub block_number: Option, @@ -73,6 +77,8 @@ impl Receipt { impl From for Receipt { fn from(r: LocalizedReceipt) -> Self { Receipt { + to: r.to.map(Into::into), + from: Some(r.from.into()), transaction_hash: Some(r.transaction_hash.into()), transaction_index: Some(r.transaction_index.into()), block_hash: Some(r.block_hash.into()), @@ -91,6 +97,8 @@ impl From for Receipt { impl From for Receipt { fn from(r: RichReceipt) -> Self { Receipt { + from: None, + to: None, transaction_hash: Some(r.transaction_hash.into()), transaction_index: Some(r.transaction_index.into()), block_hash: None, @@ -109,6 +117,8 @@ impl From for Receipt { impl From for Receipt { fn from(r: EthReceipt) -> Self { Receipt { + from: None, + to: None, transaction_hash: None, transaction_index: None, block_hash: None, @@ -131,9 +141,11 @@ mod tests { #[test] fn receipt_serialization() { - let s = r#"{"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x4510c","cumulativeGasUsed":"0x20","gasUsed":"0x10","contractAddress":null,"logs":[{"address":"0x33990122638b9132ca29c723bdf037f1a891a70c","topics":["0xa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc","0x4861736852656700000000000000000000000000000000000000000000000000"],"data":"0x","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x4510c","transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","logIndex":"0x1","transactionLogIndex":null,"type":"mined"}],"root":"0x000000000000000000000000000000000000000000000000000000000000000a","logsBloom":"0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f","status":"0x1"}"#; + let s = r#"{"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","from":null,"to":null,"blockNumber":"0x4510c","cumulativeGasUsed":"0x20","gasUsed":"0x10","contractAddress":null,"logs":[{"address":"0x33990122638b9132ca29c723bdf037f1a891a70c","topics":["0xa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc","0x4861736852656700000000000000000000000000000000000000000000000000"],"data":"0x","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x4510c","transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","logIndex":"0x1","transactionLogIndex":null,"type":"mined","removed":false}],"root":"0x000000000000000000000000000000000000000000000000000000000000000a","logsBloom":"0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f","status":"0x1"}"#; let receipt = Receipt { + from: None, + to: None, transaction_hash: Some(0.into()), transaction_index: Some(0.into()), block_hash: Some("ed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5".parse().unwrap()), @@ -155,6 +167,7 @@ mod tests { transaction_log_index: None, log_index: Some(1.into()), log_type: "mined".into(), + removed: false, }], logs_bloom: 15.into(), state_root: Some(10.into()), @@ -165,4 +178,3 @@ mod tests { assert_eq!(serialized, s); } } - diff --git a/rpc/src/v1/types/rpc_settings.rs b/rpc/src/v1/types/rpc_settings.rs index bc5bf72171c03feeb46f544d0d776e3574fc067f..3be781f20617fc6c32d00abb3d66fe6961855a95 100644 --- a/rpc/src/v1/types/rpc_settings.rs +++ b/rpc/src/v1/types/rpc_settings.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -12,7 +12,7 @@ // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity. If not, see . +// along with Parity. If not, see . //! RPC Settings data. diff --git a/rpc/src/v1/types/secretstore.rs b/rpc/src/v1/types/secretstore.rs index 4388b308ba2053c95b4c62d92738ea54ad0a0fcc..22b61b5e15266f67ffeb8f1bd6280b5ce03b11f9 100644 --- a/rpc/src/v1/types/secretstore.rs +++ b/rpc/src/v1/types/secretstore.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/types/sync.rs b/rpc/src/v1/types/sync.rs index ae421ed404749dc762a54771066f53e3f6dff14f..ec43fb27d65314e9b81a358b2a03238b45985e6e 100644 --- a/rpc/src/v1/types/sync.rs +++ b/rpc/src/v1/types/sync.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -15,7 +15,7 @@ // along with Parity. If not, see . use std::collections::BTreeMap; -use ethsync::{self, PeerInfo as SyncPeerInfo, TransactionStats as SyncTransactionStats}; +use sync::{self, PeerInfo as SyncPeerInfo, TransactionStats as SyncTransactionStats}; use serde::{Serialize, Serializer}; use v1::types::{U256, H512}; @@ -98,8 +98,8 @@ pub struct EthProtocolInfo { pub head: String, } -impl From for EthProtocolInfo { - fn from(info: ethsync::EthProtocolInfo) -> Self { +impl From for EthProtocolInfo { + fn from(info: sync::EthProtocolInfo) -> Self { EthProtocolInfo { version: info.version, difficulty: info.difficulty.map(Into::into), @@ -119,8 +119,8 @@ pub struct PipProtocolInfo { pub head: String, } -impl From for PipProtocolInfo { - fn from(info: ethsync::PipProtocolInfo) -> Self { +impl From for PipProtocolInfo { + fn from(info: sync::PipProtocolInfo) -> Self { PipProtocolInfo { version: info.version, difficulty: info.difficulty.into(), diff --git a/rpc/src/v1/types/trace.rs b/rpc/src/v1/types/trace.rs index a984c64ba80592e06cee307d24cafa2a7fe5146b..08ddfb27670d4f01a739a38dcbe9a61bc16625ea 100644 --- a/rpc/src/v1/types/trace.rs +++ b/rpc/src/v1/types/trace.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -308,6 +308,12 @@ pub enum RewardType { /// Uncle #[serde(rename="uncle")] Uncle, + /// EmptyStep (AuthorityRound) + #[serde(rename="emptyStep")] + EmptyStep, + /// External (attributed as part of an external protocol) + #[serde(rename="external")] + External, } impl From for RewardType { @@ -315,11 +321,12 @@ impl From for RewardType { match c { trace::RewardType::Block => RewardType::Block, trace::RewardType::Uncle => RewardType::Uncle, + trace::RewardType::EmptyStep => RewardType::EmptyStep, + trace::RewardType::External => RewardType::External, } } } - /// Reward action #[derive(Debug, Serialize)] pub struct Reward { diff --git a/rpc/src/v1/types/trace_filter.rs b/rpc/src/v1/types/trace_filter.rs index 3a64f5248895361ad33944f43300e4dce659b14a..83247dade02ae3376da73eba04f3f70bdc8f999d 100644 --- a/rpc/src/v1/types/trace_filter.rs +++ b/rpc/src/v1/types/trace_filter.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/types/transaction.rs b/rpc/src/v1/types/transaction.rs index c5dd63624f56f8772043244fd51be5ea429eecbe..d41fc84e0014c4e17f88a0ec3bad5ec7225c622d 100644 --- a/rpc/src/v1/types/transaction.rs +++ b/rpc/src/v1/types/transaction.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -14,12 +14,13 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +use std::sync::Arc; + use serde::{Serialize, Serializer}; use serde::ser::SerializeStruct; -use ethcore::miner; use ethcore::{contract_address, CreateContractAddress}; +use miner; use transaction::{LocalizedTransaction, Action, PendingTransaction, SignedTransaction}; -use v1::helpers::errors; use v1::types::{Bytes, H160, H256, U256, H512, U64, TransactionCondition}; /// Transaction @@ -248,17 +249,23 @@ impl Transaction { impl LocalTransactionStatus { /// Convert `LocalTransactionStatus` into RPC `LocalTransactionStatus`. - pub fn from(s: miner::LocalTransactionStatus, block_number: u64, eip86_transition: u64) -> Self { - use ethcore::miner::LocalTransactionStatus::*; + pub fn from(s: miner::pool::local_transactions::Status, block_number: u64, eip86_transition: u64) -> Self { + let convert = |tx: Arc| { + Transaction::from_signed(tx.signed().clone(), block_number, eip86_transition) + }; + use miner::pool::local_transactions::Status::*; match s { - Pending => LocalTransactionStatus::Pending, - Future => LocalTransactionStatus::Future, - Mined(tx) => LocalTransactionStatus::Mined(Transaction::from_signed(tx, block_number, eip86_transition)), - Dropped(tx) => LocalTransactionStatus::Dropped(Transaction::from_signed(tx, block_number, eip86_transition)), - Rejected(tx, err) => LocalTransactionStatus::Rejected(Transaction::from_signed(tx, block_number, eip86_transition), errors::transaction_message(err)), - Replaced(tx, gas_price, hash) => LocalTransactionStatus::Replaced(Transaction::from_signed(tx, block_number, eip86_transition), gas_price.into(), hash.into()), - Invalid(tx) => LocalTransactionStatus::Invalid(Transaction::from_signed(tx, block_number, eip86_transition)), - Canceled(tx) => LocalTransactionStatus::Canceled(Transaction::from_pending(tx, block_number, eip86_transition)), + Pending(_) => LocalTransactionStatus::Pending, + Mined(tx) => LocalTransactionStatus::Mined(convert(tx)), + Dropped(tx) => LocalTransactionStatus::Dropped(convert(tx)), + Rejected(tx, reason) => LocalTransactionStatus::Rejected(convert(tx), reason), + Invalid(tx) => LocalTransactionStatus::Invalid(convert(tx)), + Canceled(tx) => LocalTransactionStatus::Canceled(convert(tx)), + Replaced { old, new } => LocalTransactionStatus::Replaced( + convert(old), + new.signed().gas_price.into(), + new.signed().hash().into(), + ), } } } @@ -320,4 +327,3 @@ mod tests { ); } } - diff --git a/rpc/src/v1/types/transaction_condition.rs b/rpc/src/v1/types/transaction_condition.rs index 541bd364a3696bf63c697f4008afdebec22919cd..65642224c2e62b663da8c714327a0e639e84124b 100644 --- a/rpc/src/v1/types/transaction_condition.rs +++ b/rpc/src/v1/types/transaction_condition.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -64,4 +64,3 @@ mod tests { assert_eq!(transaction::Condition::Timestamp(100), TransactionCondition::Timestamp(100).into()); } } - diff --git a/rpc/src/v1/types/transaction_request.rs b/rpc/src/v1/types/transaction_request.rs index 2d4c86c7e7711e4e12ac73ec686560d9db6d2e5f..4fa47b5acd6ed5c031f7298c334452517c069a6f 100644 --- a/rpc/src/v1/types/transaction_request.rs +++ b/rpc/src/v1/types/transaction_request.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -69,14 +69,20 @@ impl fmt::Display for TransactionRequest { f, "{} ETH from {} to 0x{:?}", Colour::White.bold().paint(format_ether(eth)), - Colour::White.bold().paint(format!("0x{:?}", self.from)), + Colour::White.bold().paint( + self.from.as_ref() + .map(|f| format!("0x{:?}", f)) + .unwrap_or_else(|| "?".to_string())), to ), None => write!( f, "{} ETH from {} for contract creation", Colour::White.bold().paint(format_ether(eth)), - Colour::White.bold().paint(format!("0x{:?}", self.from)), + Colour::White.bold().paint( + self.from.as_ref() + .map(|f| format!("0x{:?}", f)) + .unwrap_or_else(|| "?".to_string())), ), } } @@ -127,7 +133,6 @@ impl Into for TransactionRequest { } } - #[cfg(test)] mod tests { use std::str::FromStr; diff --git a/rpc/src/v1/types/uint.rs b/rpc/src/v1/types/uint.rs index e887322dbae1af60c5318f72b1f8596c215cb17f..cb6dd5d3fdb1f61c893c0e5315b4b82842967e5d 100644 --- a/rpc/src/v1/types/uint.rs +++ b/rpc/src/v1/types/uint.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -55,7 +55,7 @@ macro_rules! impl_uint { impl fmt::LowerHex for $name { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:#x}", self.0) + fmt::LowerHex::fmt(&self.0, f) } } @@ -102,19 +102,19 @@ impl_uint!(U64, u64, 1); impl serde::Serialize for U128 { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer { - serializer.serialize_str(&format!("0x{:x}", self.0)) + serializer.serialize_str(&format!("{:#x}", self)) } } impl serde::Serialize for U256 { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer { - serializer.serialize_str(&format!("0x{:x}", self.0)) + serializer.serialize_str(&format!("{:#x}", self)) } } impl serde::Serialize for U64 { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer { - serializer.serialize_str(&format!("0x{:x}", self.0)) + serializer.serialize_str(&format!("{:#x}", self)) } } diff --git a/rpc/src/v1/types/work.rs b/rpc/src/v1/types/work.rs index 3664892df77200c7d8a0170c6c9d09e4d60c1ba8..5fdc117a202e0a6dfce317b25f24eda3edf20a55 100644 --- a/rpc/src/v1/types/work.rs +++ b/rpc/src/v1/types/work.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -40,4 +40,3 @@ impl Serialize for Work { } } } - diff --git a/rpc_cli/Cargo.toml b/rpc_cli/Cargo.toml index 0bda94ab674b83bc1d365df9e87a76dbf1c56dac..7d4652c38cb0f8c520543c88e503cbe26e05ba5f 100644 --- a/rpc_cli/Cargo.toml +++ b/rpc_cli/Cargo.toml @@ -9,6 +9,5 @@ version = "1.4.0" [dependencies] futures = "0.1" rpassword = "1.0" -bigint = "4.0" parity-rpc = { path = "../rpc" } parity-rpc-client = { path = "../rpc_client" } diff --git a/rpc_cli/src/lib.rs b/rpc_cli/src/lib.rs index f322129d1c137eca1394e6f4fea41b2b4fa624a8..e4554d6ed5169ca8cba24c447e55f57d2af54d1c 100644 --- a/rpc_cli/src/lib.rs +++ b/rpc_cli/src/lib.rs @@ -1,3 +1,19 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + extern crate futures; extern crate rpassword; diff --git a/rpc_client/src/client.rs b/rpc_client/src/client.rs index 8f330629b2cfa0e1860783670593f41993c7cfeb..93abdac88ed842a1a256bedf597dbdeb414bb98c 100644 --- a/rpc_client/src/client.rs +++ b/rpc_client/src/client.rs @@ -1,3 +1,19 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + use std::fmt::{Debug, Formatter, Error as FmtError}; use std::io::{BufReader, BufRead}; use std::sync::Arc; @@ -74,7 +90,7 @@ impl Handler for RpcHandler { })?; let secs = timestamp.as_secs(); let hashed = keccak(format!("{}:{}", self.auth_code, secs)); - let proto = format!("{:?}_{}", hashed, secs); + let proto = format!("{:x}_{}", hashed, secs); r.add_protocol(&proto); Ok(r) }, diff --git a/rpc_client/src/lib.rs b/rpc_client/src/lib.rs index 49f537708ef7c11534ad2d5d416b5f88c292f2bb..98614bd7634c2198efa35ee63d4e020c2649e5ef 100644 --- a/rpc_client/src/lib.rs +++ b/rpc_client/src/lib.rs @@ -1,3 +1,19 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + pub mod client; pub mod signer_client; diff --git a/rpc_client/src/signer_client.rs b/rpc_client/src/signer_client.rs index cee063109bc26e8b683773360f5232f3e6f5a7a2..e7a241137f685d5e0aa901c74ec86417497d2451 100644 --- a/rpc_client/src/signer_client.rs +++ b/rpc_client/src/signer_client.rs @@ -1,3 +1,19 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + use client::{Rpc, RpcError}; use rpc::signer::{ConfirmationRequest, TransactionModification, U256, TransactionCondition}; use serde; diff --git a/scripts/add_license.sh b/scripts/add_license.sh index 1d916f42796b53d11e8c9b0575d39ac5915bd9e6..2b283590b0bb7a8413f4862bf63b0a155cc7342b 100755 --- a/scripts/add_license.sh +++ b/scripts/add_license.sh @@ -1,6 +1,20 @@ -#!/bin/sh +#!/usr/bin/env sh -for f in $(find . -name '*.rs'); do - cat license_header $f > $f.new - mv $f.new $f +PAT_GPL="^// Copyright.*If not, see \.$" +PAT_OTHER="^// Copyright" + +for f in $(find . -type f | egrep '\.(c|cpp|rs)$'); do + HEADER=$(head -16 $f) + if [[ $HEADER =~ $PAT_GPL ]]; then + BODY=$(tail -n +17 $f) + cat license_header > temp + echo "$BODY" >> temp + mv temp $f + elif [[ $HEADER =~ $PAT_OTHER ]]; then + echo "Other license was found do nothing" + else + echo "$f was missing header" + cat license_header $f > temp + mv temp $f + fi done diff --git a/scripts/cov.sh b/scripts/cov.sh index 80fe045ca1244759e2549883bf34213c9b186f2c..b199da31aa33c6247b425fab6d12e1b6068e2315 100755 --- a/scripts/cov.sh +++ b/scripts/cov.sh @@ -12,7 +12,7 @@ ### Running coverage set -x -cargo test --all --exclude evmjit --no-run || exit $? +RUSTFLAGS="-C link-dead-code" cargo test --all --no-run || exit $? KCOV_TARGET="target/cov" KCOV_FLAGS="--verify" EXCLUDE="/usr/lib,/usr/include,$HOME/.cargo,$HOME/.multirust,rocksdb,secp256k1" diff --git a/scripts/deb-build.sh b/scripts/deb-build.sh deleted file mode 100755 index 5682e2c293b4cfbf97f61052706319c821d300b0..0000000000000000000000000000000000000000 --- a/scripts/deb-build.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/bin/bash - -set -e # fail on any error -set -u # treat unset variables as error -rm -rf deb -#create DEBIAN files -mkdir -p deb/usr/bin/ -mkdir -p deb/DEBIAN -#create copyright, docs, compat -cp LICENSE deb/DEBIAN/copyright -echo "https://github.com/paritytech/parity/wiki" >> deb/DEBIAN/docs -echo "8" >> deb/DEBIAN/compat -#create control file -control=deb/DEBIAN/control -echo "Package: parity" >> $control -version=`grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n"` -echo "Version: $version" >> $control -echo "Source: parity" >> $control -echo "Section: science" >> $control -echo "Priority: extra" >> $control -echo "Maintainer: Parity Technologies " >> $control -echo "Build-Depends: debhelper (>=9)" >> $control -echo "Standards-Version: 3.9.5" >> $control -echo "Homepage: https://parity.io" >> $control -echo "Vcs-Git: git://github.com/paritytech/parity.git" >> $control -echo "Vcs-Browser: https://github.com/paritytech/parity" >> $control -echo "Architecture: $1" >> $control -echo "Depends: libssl1.0.0 (>=1.0.0)" >> $control -echo "Description: Ethereum network client by Parity Technologies" >> $control -size=`du deb/|awk 'END {print $1}'` -echo "Installed-Size: $size" >> $control -#build .deb package - -exit diff --git a/scripts/doc.sh b/scripts/doc.sh index ae209ad46f979c47994fa06a23eca09665b682e4..f0022610a54f78f8df3d02ae19c0d1c6f3b41ef4 100755 --- a/scripts/doc.sh +++ b/scripts/doc.sh @@ -1,5 +1,5 @@ #!/bin/sh # generate documentation only for partiy and ethcore libraries -cargo doc --no-deps --verbose --all --exclude parity-ipfs-api --exclude evmjit && +cargo doc --no-deps --verbose --all --exclude parity-ipfs-api && echo '' > target/doc/index.html diff --git a/scripts/gitlab-build.sh b/scripts/gitlab-build.sh index a2a084adc9b2effeed8b88c0c6a151bb3ddf5e8a..5e272f6216ce7778489bc44e211e8b22f756bf55 100755 --- a/scripts/gitlab-build.sh +++ b/scripts/gitlab-build.sh @@ -17,14 +17,12 @@ echo "Build identifier: " $IDENT echo "Cargo target: " $PLATFORM echo "CC&CXX flags: " $CC ", " $CXX echo "Architecture: " $ARC -echo "Libssl version: " $LIBSSL echo "Parity version: " $VER echo "Branch: " $CI_BUILD_REF_NAME echo "--------------------" -# NOTE for md5 and sha256 we want to display filename as well +# NOTE for sha256 we want to display filename as well # hence we use --* instead of -p * -MD5_BIN="rhash --md5" SHA256_BIN="rhash --sha256" set_env () { @@ -44,7 +42,6 @@ set_env_win () { set RUST_BACKTRACE=1 #export RUSTFLAGS=$RUSTFLAGS rustup default stable-x86_64-pc-windows-msvc - echo "MsBuild.exe windows\ptray\ptray.vcxproj /p:Platform=x64 /p:Configuration=Release" > msbuild.cmd echo "@ signtool sign /f "\%"1 /p "\%"2 /tr http://timestamp.comodoca.com /du https://parity.io "\%"3" > sign.cmd } build () { @@ -62,120 +59,41 @@ build () { cargo build --target $PLATFORM --release -p ethstore-cli echo "Build ethkey-cli:" cargo build --target $PLATFORM --release -p ethkey-cli + echo "Build whisper-cli:" + cargo build --target $PLATFORM --release -p whisper-cli } strip_binaries () { echo "Strip binaries:" $STRIP_BIN -v target/$PLATFORM/release/parity $STRIP_BIN -v target/$PLATFORM/release/parity-evm $STRIP_BIN -v target/$PLATFORM/release/ethstore - $STRIP_BIN -v target/$PLATFORM/release/ethkey; + $STRIP_BIN -v target/$PLATFORM/release/ethkey + $STRIP_BIN -v target/$PLATFORM/release/whisper; } calculate_checksums () { echo "Checksum calculation:" rhash --version - rm -rf *.md5 rm -rf *.sha256 BIN="target/$PLATFORM/release/parity$S3WIN" export SHA3="$($BIN tools hash $BIN)" echo "Parity file SHA3: $SHA3" - $MD5_BIN target/$PLATFORM/release/parity$S3WIN > parity$S3WIN.md5 $SHA256_BIN target/$PLATFORM/release/parity$S3WIN > parity$S3WIN.sha256 - $MD5_BIN target/$PLATFORM/release/parity-evm$S3WIN > parity-evm$S3WIN.md5 $SHA256_BIN target/$PLATFORM/release/parity-evm$S3WIN > parity-evm$S3WIN.sha256 - $MD5_BIN target/$PLATFORM/release/ethstore$S3WIN > ethstore$S3WIN.md5 $SHA256_BIN target/$PLATFORM/release/ethstore$S3WIN > ethstore$S3WIN.sha256 - $MD5_BIN target/$PLATFORM/release/ethkey$S3WIN > ethkey$S3WIN.md5 $SHA256_BIN target/$PLATFORM/release/ethkey$S3WIN > ethkey$S3WIN.sha256 -} -make_deb () { - rm -rf deb - echo "create DEBIAN files" - mkdir -p deb/usr/bin/ - mkdir -p deb/DEBIAN - echo "create copyright, docs, compat" - cp LICENSE deb/DEBIAN/copyright - echo "https://github.com/paritytech/parity/wiki" >> deb/DEBIAN/docs - echo "8" >> deb/DEBIAN/compat - echo "create control file" - control=deb/DEBIAN/control - echo "Package: parity" >> $control - echo "Version: $VER" >> $control - echo "Source: parity" >> $control - echo "Section: science" >> $control - echo "Priority: extra" >> $control - echo "Maintainer: Parity Technologies " >> $control - echo "Build-Depends: debhelper (>=9)" >> $control - echo "Standards-Version: 3.9.5" >> $control - echo "Homepage: https://parity.io" >> $control - echo "Vcs-Git: git://github.com/paritytech/parity.git" >> $control - echo "Vcs-Browser: https://github.com/paritytech/parity" >> $control - echo "Architecture: $ARC" >> $control - echo "Depends: $LIBSSL" >> $control - echo "Description: Ethereum network client by Parity Technologies" >> $control - size=`du deb/|awk 'END {print $1}'` - echo "Installed-Size: $size" >> $control - echo "build .deb package" - cp target/$PLATFORM/release/parity deb/usr/bin/parity - cp target/$PLATFORM/release/parity-evm deb/usr/bin/parity-evm - cp target/$PLATFORM/release/ethstore deb/usr/bin/ethstore - cp target/$PLATFORM/release/ethkey deb/usr/bin/ethkey - dpkg-deb -b deb "parity_"$VER"_"$IDENT"_"$ARC".deb" - $MD5_BIN "parity_"$VER"_"$IDENT"_"$ARC".deb" > "parity_"$VER"_"$IDENT"_"$ARC".deb.md5" - $SHA256_BIN "parity_"$VER"_"$IDENT"_"$ARC".deb" > "parity_"$VER"_"$IDENT"_"$ARC".deb.sha256" -} -make_rpm () { - rm -rf /install - mkdir -p /install/usr/bin - cp target/$PLATFORM/release/parity /install/usr/bin - cp target/$PLATFORM/release/parity-evm /install/usr/bin/parity-evm - cp target/$PLATFORM/release/ethstore /install/usr/bin/ethstore - cp target/$PLATFORM/release/ethkey /install/usr/bin/ethkey - - rm -rf "parity-"$VER"-1."$ARC".rpm" || true - fpm -s dir -t rpm -n parity -v $VER --epoch 1 --license GPLv3 -d openssl --provides parity --url https://parity.io --vendor "Parity Technologies" -a x86_64 -m "" --description "Ethereum network client by Parity Technologies" -C /install/ - cp "parity-"$VER"-1."$ARC".rpm" "parity_"$VER"_"$IDENT"_"$ARC".rpm" - $MD5_BIN "parity_"$VER"_"$IDENT"_"$ARC".rpm" > "parity_"$VER"_"$IDENT"_"$ARC".rpm.md5" - $SHA256_BIN "parity_"$VER"_"$IDENT"_"$ARC".rpm" > "parity_"$VER"_"$IDENT"_"$ARC".rpm.sha256" -} -make_pkg () { - echo "make PKG" - cp target/$PLATFORM/release/parity target/release/parity - cp target/$PLATFORM/release/parity-evm target/release/parity-evm - cp target/$PLATFORM/release/ethstore target/release/ethstore - cp target/$PLATFORM/release/ethkey target/release/ethkey - cd mac - xcodebuild -configuration Release - cd .. - packagesbuild -v mac/Parity.pkgproj - productsign --sign 'Developer ID Installer: PARITY TECHNOLOGIES LIMITED (P2PX3JU8FT)' target/release/Parity\ Ethereum.pkg target/release/Parity\ Ethereum-signed.pkg - mv target/release/Parity\ Ethereum-signed.pkg "parity_"$VER"_"$IDENT"_"$ARC".pkg" - $MD5_BIN "parity_"$VER"_"$IDENT"_"$ARC"."$EXT >> "parity_"$VER"_"$IDENT"_"$ARC".pkg.md5" - $SHA256_BIN "parity_"$VER"_"$IDENT"_"$ARC"."$EXT >> "parity_"$VER"_"$IDENT"_"$ARC".pkg.sha256" + $SHA256_BIN target/$PLATFORM/release/whisper$S3WIN > whisper$S3WIN.sha256 } sign_exe () { ./sign.cmd $keyfile $certpass "target/$PLATFORM/release/parity.exe" } -make_exe () { - ./msbuild.cmd - ./sign.cmd $keyfile $certpass windows/ptray/x64/release/ptray.exe - cd nsis - curl -sL --url "https://github.com/paritytech/win-build/raw/master/vc_redist.x64.exe" -o vc_redist.x64.exe - echo "makensis.exe installer.nsi" > nsis.cmd - ./nsis.cmd - cd .. - cp nsis/installer.exe "parity_"$VER"_"$IDENT"_"$ARC"."$EXT - ./sign.cmd $keyfile $certpass "parity_"$VER"_"$IDENT"_"$ARC"."$EXT - $MD5_BIN "parity_"$VER"_"$IDENT"_"$ARC"."$EXT -p %h > "parity_"$VER"_"$IDENT"_"$ARC"."$EXT".md5" - $SHA256_BIN "parity_"$VER"_"$IDENT"_"$ARC"."$EXT -p %h > "parity_"$VER"_"$IDENT"_"$ARC"."$EXT".sha256" -} push_binaries () { echo "Push binaries to AWS S3" aws configure set aws_access_key_id $s3_key aws configure set aws_secret_access_key $s3_secret - if [[ "$CI_BUILD_REF_NAME" = "master" || "$CI_BUILD_REF_NAME" = "beta" || "$CI_BUILD_REF_NAME" = "stable" || "$CI_BUILD_REF_NAME" = "nightly" ]]; + if [[ "$CI_BUILD_REF_NAME" = "beta" || "$CI_BUILD_REF_NAME" = "stable" || "$CI_BUILD_REF_NAME" = "nightly" ]]; then export S3_BUCKET=builds-parity-published; else @@ -183,25 +101,21 @@ push_binaries () { fi aws s3 rm --recursive s3://$S3_BUCKET/$CI_BUILD_REF_NAME/$BUILD_PLATFORM aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$BUILD_PLATFORM/parity$S3WIN --body target/$PLATFORM/release/parity$S3WIN - aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$BUILD_PLATFORM/parity$S3WIN.md5 --body parity$S3WIN.md5 aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$BUILD_PLATFORM/parity$S3WIN.sha256 --body parity$S3WIN.sha256 aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$BUILD_PLATFORM/parity-evm$S3WIN --body target/$PLATFORM/release/parity-evm$S3WIN - aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$BUILD_PLATFORM/parity-evm$S3WIN.md5 --body parity-evm$S3WIN.md5 aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$BUILD_PLATFORM/parity-evm$S3WIN.sha256 --body parity-evm$S3WIN.sha256 aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$BUILD_PLATFORM/ethstore$S3WIN --body target/$PLATFORM/release/ethstore$S3WIN - aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$BUILD_PLATFORM/ethstore$S3WIN.md5 --body ethstore$S3WIN.md5 aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$BUILD_PLATFORM/ethstore$S3WIN.sha256 --body ethstore$S3WIN.sha256 aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$BUILD_PLATFORM/ethkey$S3WIN --body target/$PLATFORM/release/ethkey$S3WIN - aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$BUILD_PLATFORM/ethkey$S3WIN.md5 --body ethkey$S3WIN.md5 aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$BUILD_PLATFORM/ethkey$S3WIN.sha256 --body ethkey$S3WIN.sha256 - aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$BUILD_PLATFORM/"parity_"$VER"_"$IDENT"_"$ARC"."$EXT --body "parity_"$VER"_"$IDENT"_"$ARC"."$EXT - aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$BUILD_PLATFORM/"parity_"$VER"_"$IDENT"_"$ARC"."$EXT".md5" --body "parity_"$VER"_"$IDENT"_"$ARC"."$EXT".md5" - aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$BUILD_PLATFORM/"parity_"$VER"_"$IDENT"_"$ARC"."$EXT".sha256" --body "parity_"$VER"_"$IDENT"_"$ARC"."$EXT".sha256" + aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$BUILD_PLATFORM/whisper$S3WIN --body target/$PLATFORM/release/whisper$S3WIN + aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$BUILD_PLATFORM/whisper$S3WIN.sha256 --body whisper$S3WIN.sha256 } + make_archive () { echo "add artifacts to archive" rm -rf parity.zip - zip -r parity.zip target/$PLATFORM/release/parity$S3WIN target/$PLATFORM/release/parity-evm$S3WIN target/$PLATFORM/release/ethstore$S3WIN target/$PLATFORM/release/ethkey$S3WIN parity$S3WIN.md5 parity-evm$S3WIN.md5 ethstore$S3WIN.md5 ethkey$S3WIN.md5 parity$S3WIN.sha256 parity-evm$S3WIN.sha256 ethstore$S3WIN.sha256 ethkey$S3WIN.sha256 + zip -r parity.zip target/$PLATFORM/release/parity$S3WIN target/$PLATFORM/release/parity-evm$S3WIN target/$PLATFORM/release/ethstore$S3WIN target/$PLATFORM/release/ethkey$S3WIN target/$PLATFORM/release/whisper$S3WIN parity$S3WIN.sha256 parity-evm$S3WIN.sha256 ethstore$S3WIN.sha256 ethkey$S3WIN.sha256 whisper$S3WIN.sha256 } updater_push_release () { @@ -219,89 +133,55 @@ case $BUILD_PLATFORM in #set strip bin STRIP_BIN="strip" #package extention - EXT="deb" build strip_binaries calculate_checksums - make_deb make_archive push_binaries updater_push_release ;; - x86_64-unknown-debian-gnu) - STRIP_BIN="strip" - EXT="deb" - LIBSSL="libssl1.1 (>=1.1.0)" - echo "Use libssl1.1 (>=1.1.0) for Debian builds" - build - strip_binaries - calculate_checksums - make_deb - make_archive - push_binaries - ;; - x86_64-unknown-centos-gnu) - STRIP_BIN="strip" - EXT="rpm" - build - strip_binaries - calculate_checksums - make_rpm - make_archive - push_binaries - ;; i686-unknown-linux-gnu) STRIP_BIN="strip" - EXT="deb" set_env build strip_binaries calculate_checksums - make_deb make_archive push_binaries ;; armv7-unknown-linux-gnueabihf) STRIP_BIN="arm-linux-gnueabihf-strip" - EXT="deb" set_env build strip_binaries calculate_checksums - make_deb make_archive push_binaries ;; arm-unknown-linux-gnueabihf) STRIP_BIN="arm-linux-gnueabihf-strip" - EXT="deb" set_env build strip_binaries calculate_checksums - make_deb make_archive push_binaries ;; aarch64-unknown-linux-gnu) STRIP_BIN="aarch64-linux-gnu-strip" - EXT="deb" set_env build strip_binaries calculate_checksums - make_deb make_archive push_binaries ;; x86_64-apple-darwin) STRIP_BIN="strip" PLATFORM="x86_64-apple-darwin" - EXT="pkg" build strip_binaries calculate_checksums - make_pkg make_archive push_binaries updater_push_release @@ -309,11 +189,12 @@ case $BUILD_PLATFORM in x86_64-unknown-snap-gnu) ARC="amd64" EXT="snap" + apt update apt install -y expect zip rhash snapcraft clean echo "Prepare snapcraft.yaml for build on Gitlab CI in Docker image" sed -i 's/git/'"$VER"'/g' snap/snapcraft.yaml - if [[ "$CI_BUILD_REF_NAME" = "beta" || "$VER" == *1.10* ]]; + if [[ "$CI_BUILD_REF_NAME" = "beta" || "$VER" == *1.11* ]]; then sed -i -e 's/grade: devel/grade: stable/' snap/snapcraft.yaml; fi @@ -331,11 +212,10 @@ case $BUILD_PLATFORM in snapcraft push "parity_"$VER"_amd64.snap" snapcraft status parity snapcraft logout - $MD5_BIN "parity_"$VER"_amd64.snap" > "parity_"$VER"_amd64.snap.md5" $SHA256_BIN "parity_"$VER"_amd64.snap" > "parity_"$VER"_amd64.snap.sha256" echo "add artifacts to archive" rm -rf parity.zip - zip -r parity.zip "parity_"$VER"_amd64.snap" "parity_"$VER"_amd64.snap.md5" "parity_"$VER"_amd64.snap.sha256" + zip -r parity.zip "parity_"$VER"_amd64.snap" "parity_"$VER"_amd64.snap.sha256" ;; x86_64-pc-windows-msvc) set_env_win @@ -344,7 +224,6 @@ case $BUILD_PLATFORM in build sign_exe calculate_checksums - make_exe make_archive push_binaries updater_push_release diff --git a/scripts/gitlab-test.sh b/scripts/gitlab-test.sh index 812317a2704fe29b1554277680e41d10006f98eb..57cf7c6b6c50d91fcc8ed5f5e83c3630667ce223 100755 --- a/scripts/gitlab-test.sh +++ b/scripts/gitlab-test.sh @@ -2,13 +2,13 @@ #ARGUMENT test for RUST and COVERAGE set -e # fail on any error set -u # treat unset variables as error -if [[ "$CI_COMMIT_REF_NAME" = "beta" || "$CI_COMMIT_REF_NAME" = "stable" ]]; then +if [[ "$CI_COMMIT_REF_NAME" = "master" || "$CI_COMMIT_REF_NAME" = "beta" || "$CI_COMMIT_REF_NAME" = "stable" ]]; then export GIT_COMPARE=$CI_COMMIT_REF_NAME~; else export GIT_COMPARE=master; fi -export RUST_FILES_MODIFIED="$(git --no-pager diff --name-only $GIT_COMPARE...$CI_COMMIT_SHA | grep -v -e ^\\. -e ^LICENSE -e ^README.md -e ^test.sh -e ^windows/ -e ^scripts/ -e ^mac/ -e ^nsis/ | wc -l)" -echo "RUST_FILES_MODIFIED: $RUST_FILES_MODIFIED" +git fetch -a +export RUST_FILES_MODIFIED="$(git --no-pager diff --name-only $GIT_COMPARE...$CI_COMMIT_SHA | grep -v -e ^\\. -e ^LICENSE -e ^README.md -e ^test.sh -e ^windows/ -e ^scripts/ -e ^mac/ -e ^nsis/ -e ^docs/ | wc -l)" echo "RUST_FILES_MODIFIED: $RUST_FILES_MODIFIED" TEST_SWITCH=$1 rust_test () { diff --git a/scripts/hook.sh b/scripts/hook.sh index cb8085a028262e40138e3aa18bd4e6f7a75a2d9d..2d64d5782f215cf9289390eb4ac4b653e438d7ac 100755 --- a/scripts/hook.sh +++ b/scripts/hook.sh @@ -7,6 +7,6 @@ echo "set -e" >> $FILE # Run release build echo "cargo build --features dev" >> $FILE # Build tests -echo "cargo test --no-run --features dev --all --exclude parity-ipfs-api --exclude evmjit" >> $FILE +echo "cargo test --no-run --features dev --all --exclude parity-ipfs-api" >> $FILE echo "" >> $FILE chmod +x $FILE diff --git a/scripts/remove_duplicate_empty_lines.sh b/scripts/remove_duplicate_empty_lines.sh new file mode 100755 index 0000000000000000000000000000000000000000..0df265ab9f601064dcd15ec7cc6e25c602080e54 --- /dev/null +++ b/scripts/remove_duplicate_empty_lines.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env sh + +for f in $(find . -name '*.rs'); do + cat -s $f > $f.temp + mv $f.temp $f +done diff --git a/secret_store/Cargo.toml b/secret_store/Cargo.toml index 10b690b6d99b27667be4f45e9526f6da24e6c047..a8aed8312735721db48e71323ed9efeeaabe150e 100644 --- a/secret_store/Cargo.toml +++ b/secret_store/Cargo.toml @@ -9,28 +9,29 @@ authors = ["Parity Technologies "] byteorder = "1.0" log = "0.3" parking_lot = "0.5" -hyper = { version = "0.10", default-features = false } +hyper = { version = "0.11", default-features = false } serde = "1.0" serde_json = "1.0" serde_derive = "1.0" futures = "0.1" futures-cpupool = "0.1" rustc-hex = "1.0" -tiny-keccak = "1.3" -tokio-core = "0.1.6" -tokio-io = "0.1.0" +tiny-keccak = "1.4" +tokio = "0.1" +tokio-core = "0.1" +tokio-io = "0.1" tokio-service = "0.1" tokio-proto = "0.1" url = "1.0" ethcore = { path = "../ethcore" } ethcore-bytes = { path = "../util/bytes" } -ethereum-types = "0.2" -ethsync = { path = "../sync" } +ethcore-crypto = { path = "../ethcore/crypto" } +ethcore-logger = { path = "../logger" } +ethcore-sync = { path = "../ethcore/sync" } +ethcore-transaction = { path = "../ethcore/transaction" } +ethereum-types = "0.3" kvdb = { path = "../util/kvdb" } -kvdb-rocksdb = { path = "../util/kvdb-rocksdb" } keccak-hash = { path = "../util/hash" } -ethcore-logger = { path = "../logger" } -ethcrypto = { path = "../ethcrypto" } ethkey = { path = "../ethkey" } lazy_static = "1.0" ethabi = "5.1" @@ -38,4 +39,6 @@ ethabi-derive = "5.0" ethabi-contract = "5.0" [dev-dependencies] +ethcore = { path = "../ethcore", features = ["test-helpers"] } tempdir = "0.3" +kvdb-rocksdb = { path = "../util/kvdb-rocksdb" } diff --git a/secret_store/res/key_server_set.json b/secret_store/res/key_server_set.json index 0fb9502b1143d3d9a08081410e0f4fe5bf59a314..28530e3532bed7cce098300b161ae9f3db899908 100644 --- a/secret_store/res/key_server_set.json +++ b/secret_store/res/key_server_set.json @@ -1 +1,24 @@ -[{"constant":true,"inputs":[],"name":"getMigrationMaster","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"keyServer","type":"address"}],"name":"getMigrationKeyServerPublic","outputs":[{"name":"","type":"bytes"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"id","type":"bytes32"}],"name":"startMigration","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"keyServer","type":"address"}],"name":"getMigrationKeyServerAddress","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getMigrationId","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getNewKeyServers","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"id","type":"bytes32"}],"name":"confirmMigration","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getMigrationKeyServers","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"keyServer","type":"address"}],"name":"isMigrationConfirmed","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getCurrentKeyServers","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"keyServer","type":"address"}],"name":"getCurrentKeyServerPublic","outputs":[{"name":"","type":"bytes"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"keyServer","type":"address"}],"name":"getNewKeyServerAddress","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"keyServer","type":"address"}],"name":"getCurrentKeyServerAddress","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"keyServer","type":"address"}],"name":"getNewKeyServerPublic","outputs":[{"name":"","type":"bytes"}],"payable":false,"stateMutability":"view","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"keyServer","type":"address"}],"name":"KeyServerAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"keyServer","type":"address"}],"name":"KeyServerRemoved","type":"event"},{"anonymous":false,"inputs":[],"name":"MigrationStarted","type":"event"},{"anonymous":false,"inputs":[],"name":"MigrationCompleted","type":"event"}] \ No newline at end of file +[ + {"constant":true,"inputs":[],"name":"getMigrationMaster","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"}, + {"constant":true,"inputs":[{"name":"keyServer","type":"address"}],"name":"getMigrationKeyServerPublic","outputs":[{"name":"","type":"bytes"}],"payable":false,"stateMutability":"view","type":"function"}, + {"constant":false,"inputs":[{"name":"id","type":"bytes32"}],"name":"startMigration","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}, + {"constant":true,"inputs":[{"name":"keyServer","type":"address"}],"name":"getCurrentKeyServerIndex","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"}, + {"constant":true,"inputs":[{"name":"keyServer","type":"address"}],"name":"getMigrationKeyServerAddress","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"}, + {"constant":true,"inputs":[],"name":"getMigrationId","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"}, + {"constant":true,"inputs":[],"name":"getNewKeyServers","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"}, + {"constant":false,"inputs":[{"name":"id","type":"bytes32"}],"name":"confirmMigration","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}, + {"constant":true,"inputs":[],"name":"getMigrationKeyServers","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"}, + {"constant":true,"inputs":[{"name":"keyServer","type":"address"}],"name":"isMigrationConfirmed","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"}, + {"constant":true,"inputs":[],"name":"getCurrentKeyServersCount","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"}, + {"constant":true,"inputs":[],"name":"getCurrentKeyServers","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"}, + {"constant":true,"inputs":[],"name":"getCurrentLastChange","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}, + {"constant":true,"inputs":[{"name":"keyServer","type":"address"}],"name":"getCurrentKeyServerPublic","outputs":[{"name":"","type":"bytes"}],"payable":false,"stateMutability":"view","type":"function"}, + {"constant":true,"inputs":[{"name":"keyServer","type":"address"}],"name":"getNewKeyServerAddress","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"}, + {"constant":true,"inputs":[{"name":"keyServer","type":"address"}],"name":"getCurrentKeyServerAddress","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"}, + {"constant":true,"inputs":[{"name":"keyServer","type":"address"}],"name":"getNewKeyServerPublic","outputs":[{"name":"","type":"bytes"}],"payable":false,"stateMutability":"view","type":"function"}, + {"constant":true,"inputs":[{"name":"index","type":"uint8"}],"name":"getCurrentKeyServer","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"}, + {"anonymous":false,"inputs":[{"indexed":false,"name":"keyServer","type":"address"}],"name":"KeyServerAdded","type":"event"}, + {"anonymous":false,"inputs":[{"indexed":false,"name":"keyServer","type":"address"}],"name":"KeyServerRemoved","type":"event"}, + {"anonymous":false,"inputs":[],"name":"MigrationStarted","type":"event"}, + {"anonymous":false,"inputs":[],"name":"MigrationCompleted","type":"event"} +] \ No newline at end of file diff --git a/secret_store/res/service.json b/secret_store/res/service.json index 37d45350b2a34ee8f730a2a9762150bb9d629cdb..d79c38e7aac6c5181469e2114812634ffd4d4569 100644 --- a/secret_store/res/service.json +++ b/secret_store/res/service.json @@ -1,8 +1,33 @@ [ + {"constant":true,"inputs":[{"name":"keyServer","type":"address"}],"name":"requireKeyServer","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"}, + {"constant":true,"inputs":[],"name":"serverKeyGenerationRequestsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":true,"inputs":[{"name":"index","type":"uint256"}],"name":"getServerKeyId","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":false,"inputs":[{"name":"serverKeyId","type":"bytes32"},{"name":"serverKeyPublic","type":"bytes"},{"name":"v","type":"uint8"},{"name":"r","type":"bytes32"},{"name":"s","type":"bytes32"}],"name":"serverKeyGenerated","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}, - {"constant":true,"inputs":[{"name":"serverKeyId","type":"bytes32"}],"name":"getServerKeyThreshold","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":true,"inputs":[{"name":"serverKeyId","type":"bytes32"},{"name":"authority","type":"address"}],"name":"getServerKeyConfirmationStatus","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"}, - {"anonymous":false,"inputs":[{"indexed":true,"name":"serverKeyId","type":"bytes32"},{"indexed":true,"name":"threshold","type":"uint256"}],"name":"ServerKeyRequested","type":"event"} + {"constant":false,"inputs":[{"name":"serverKeyId","type":"bytes32"}],"name":"serverKeyGenerationError","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}, + {"constant":true,"inputs":[{"name":"index","type":"uint256"}],"name":"getServerKeyGenerationRequest","outputs":[{"name":"","type":"bytes32"},{"name":"","type":"address"},{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}, + {"constant":false,"inputs":[{"name":"serverKeyId","type":"bytes32"},{"name":"serverKeyPublic","type":"bytes"}],"name":"serverKeyGenerated","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}, + {"constant":true,"inputs":[{"name":"serverKeyId","type":"bytes32"},{"name":"keyServer","type":"address"}],"name":"isServerKeyGenerationResponseRequired","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"}, + {"anonymous":false,"inputs":[{"indexed":false,"name":"serverKeyId","type":"bytes32"},{"indexed":false,"name":"author","type":"address"},{"indexed":false,"name":"threshold","type":"uint8"}],"name":"ServerKeyGenerationRequested","type":"event"}, + + {"constant":false,"inputs":[{"name":"serverKeyId","type":"bytes32"}],"name":"serverKeyRetrievalError","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}, + {"constant":true,"inputs":[],"name":"serverKeyRetrievalRequestsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}, + {"constant":true,"inputs":[{"name":"serverKeyId","type":"bytes32"},{"name":"keyServer","type":"address"}],"name":"isServerKeyRetrievalResponseRequired","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"}, + {"constant":true,"inputs":[{"name":"index","type":"uint256"}],"name":"getServerKeyRetrievalRequest","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"}, + {"constant":false,"inputs":[{"name":"serverKeyId","type":"bytes32"},{"name":"serverKeyPublic","type":"bytes"},{"name":"threshold","type":"uint8"}],"name":"serverKeyRetrieved","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}, + {"anonymous":false,"inputs":[{"indexed":false,"name":"serverKeyId","type":"bytes32"}],"name":"ServerKeyRetrievalRequested","type":"event"}, + + {"constant":true,"inputs":[],"name":"documentKeyStoreRequestsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}, + {"constant":false,"inputs":[{"name":"serverKeyId","type":"bytes32"}],"name":"documentKeyStoreError","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}, + {"constant":false,"inputs":[{"name":"serverKeyId","type":"bytes32"}],"name":"documentKeyStored","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}, + {"constant":true,"inputs":[{"name":"serverKeyId","type":"bytes32"},{"name":"keyServer","type":"address"}],"name":"isDocumentKeyStoreResponseRequired","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"}, + {"constant":true,"inputs":[{"name":"index","type":"uint256"}],"name":"getDocumentKeyStoreRequest","outputs":[{"name":"","type":"bytes32"},{"name":"","type":"address"},{"name":"","type":"bytes"},{"name":"","type":"bytes"}],"payable":false,"stateMutability":"view","type":"function"}, + {"anonymous":false,"inputs":[{"indexed":false,"name":"serverKeyId","type":"bytes32"},{"indexed":false,"name":"author","type":"address"},{"indexed":false,"name":"commonPoint","type":"bytes"},{"indexed":false,"name":"encryptedPoint","type":"bytes"}],"name":"DocumentKeyStoreRequested","type":"event"}, + + {"constant":false,"inputs":[{"name":"serverKeyId","type":"bytes32"},{"name":"requester","type":"address"},{"name":"commonPoint","type":"bytes"},{"name":"threshold","type":"uint8"}],"name":"documentKeyCommonRetrieved","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}, + {"constant":true,"inputs":[{"name":"serverKeyId","type":"bytes32"},{"name":"keyServer","type":"address"},{"name":"requester","type":"address"}],"name":"isDocumentKeyShadowRetrievalResponseRequired","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"}, + {"constant":false,"inputs":[{"name":"serverKeyId","type":"bytes32"},{"name":"requester","type":"address"},{"name":"participants","type":"uint256"},{"name":"decryptedSecret","type":"bytes"},{"name":"shadow","type":"bytes"}],"name":"documentKeyPersonalRetrieved","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}, + {"constant":false,"inputs":[{"name":"serverKeyId","type":"bytes32"},{"name":"requester","type":"address"}],"name":"documentKeyShadowRetrievalError","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}, + {"constant":true,"inputs":[],"name":"documentKeyShadowRetrievalRequestsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}, + {"constant":true,"inputs":[{"name":"index","type":"uint256"}],"name":"getDocumentKeyShadowRetrievalRequest","outputs":[{"name":"","type":"bytes32"},{"name":"","type":"bytes"},{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"}, + {"anonymous":false,"inputs":[{"indexed":false,"name":"serverKeyId","type":"bytes32"},{"indexed":false,"name":"requester","type":"address"}],"name":"DocumentKeyCommonRetrievalRequested","type":"event"}, + {"anonymous":false,"inputs":[{"indexed":false,"name":"serverKeyId","type":"bytes32"},{"indexed":false,"name":"requesterPublic","type":"bytes"}],"name":"DocumentKeyPersonalRetrievalRequested","type":"event"} ] \ No newline at end of file diff --git a/secret_store/src/acl_storage.rs b/secret_store/src/acl_storage.rs index f3d116145f842eedee2c3dca7d1c572b38949910..efc01f0307166fdc44caa6fe75b06d83cadf4935 100644 --- a/secret_store/src/acl_storage.rs +++ b/secret_store/src/acl_storage.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -16,13 +16,13 @@ use std::sync::Arc; use std::collections::{HashMap, HashSet}; +use std::time::Duration; use parking_lot::{Mutex, RwLock}; -use ethkey::public_to_address; -use ethcore::client::{BlockId, ChainNotify, CallContract, RegistryInfo}; +use ethcore::client::{BlockId, ChainNotify, ChainRoute, CallContract}; use ethereum_types::{H256, Address}; use bytes::Bytes; use trusted_client::TrustedClient; -use types::all::{Error, ServerKeyId, Public}; +use types::{Error, ServerKeyId, ContractAddress}; use_contract!(acl_storage, "AclStorage", "res/acl_storage.json"); @@ -30,8 +30,8 @@ const ACL_CHECKER_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_acl_checke /// ACL storage of Secret Store pub trait AclStorage: Send + Sync { - /// Check if requestor with `public` key can access document with hash `document` - fn check(&self, public: &Public, document: &ServerKeyId) -> Result; + /// Check if requestor can access document with hash `document` + fn check(&self, requester: Address, document: &ServerKeyId) -> Result; } /// On-chain ACL storage implementation. @@ -44,8 +44,10 @@ pub struct OnChainAclStorage { struct CachedContract { /// Blockchain client. client: TrustedClient, - /// Contract address. - contract_addr: Option
, + /// Contract address source. + address_source: ContractAddress, + /// Current contract address. + contract_address: Option
, /// Contract at given address. contract: acl_storage::AclStorage, } @@ -53,68 +55,68 @@ struct CachedContract { /// Dummy ACL storage implementation (check always passed). #[derive(Default, Debug)] pub struct DummyAclStorage { - prohibited: RwLock>>, + prohibited: RwLock>>, } impl OnChainAclStorage { - pub fn new(trusted_client: TrustedClient) -> Result, Error> { + pub fn new(trusted_client: TrustedClient, address_source: ContractAddress) -> Result, Error> { let client = trusted_client.get_untrusted(); let acl_storage = Arc::new(OnChainAclStorage { - contract: Mutex::new(CachedContract::new(trusted_client)), + contract: Mutex::new(CachedContract::new(trusted_client, address_source)), }); client - .ok_or(Error::Internal("Constructing OnChainAclStorage without active Client".into()))? + .ok_or_else(|| Error::Internal("Constructing OnChainAclStorage without active Client".into()))? .add_notify(acl_storage.clone()); Ok(acl_storage) } } impl AclStorage for OnChainAclStorage { - fn check(&self, public: &Public, document: &ServerKeyId) -> Result { - self.contract.lock().check(public, document) + fn check(&self, requester: Address, document: &ServerKeyId) -> Result { + self.contract.lock().check(requester, document) } } impl ChainNotify for OnChainAclStorage { - fn new_blocks(&self, _imported: Vec, _invalid: Vec, enacted: Vec, retracted: Vec, _sealed: Vec, _proposed: Vec, _duration: u64) { - if !enacted.is_empty() || !retracted.is_empty() { - self.contract.lock().update() + fn new_blocks(&self, _imported: Vec, _invalid: Vec, route: ChainRoute, _sealed: Vec, _proposed: Vec, _duration: Duration) { + if !route.enacted().is_empty() || !route.retracted().is_empty() { + self.contract.lock().update_contract_address() } } } impl CachedContract { - pub fn new(client: TrustedClient) -> Self { - CachedContract { + pub fn new(client: TrustedClient, address_source: ContractAddress) -> Self { + let mut contract = CachedContract { client, - contract_addr: None, + address_source, + contract_address: None, contract: acl_storage::AclStorage::default(), - } + }; + contract.update_contract_address(); + contract } - pub fn update(&mut self) { - if let Some(client) = self.client.get() { - match client.registry_address(ACL_CHECKER_CONTRACT_REGISTRY_NAME.to_owned(), BlockId::Latest) { - Some(new_contract_addr) if Some(new_contract_addr).as_ref() != self.contract_addr.as_ref() => { - trace!(target: "secretstore", "Configuring for ACL checker contract from {}", new_contract_addr); - self.contract_addr = Some(new_contract_addr); - }, - Some(_) | None => () - } + pub fn update_contract_address(&mut self) { + let contract_address = self.client.read_contract_address(ACL_CHECKER_CONTRACT_REGISTRY_NAME.into(), &self.address_source); + if contract_address != self.contract_address { + trace!(target: "secretstore", "Configuring for ACL checker contract from address {:?}", + contract_address); + + self.contract_address = contract_address; } } - pub fn check(&mut self, public: &Public, document: &ServerKeyId) -> Result { + pub fn check(&mut self, requester: Address, document: &ServerKeyId) -> Result { if let Some(client) = self.client.get() { // call contract to check accesss - match self.contract_addr { + match self.contract_address { Some(contract_address) => { - let address = public_to_address(&public); let do_call = |data| client.call_contract(BlockId::Latest, contract_address, data); self.contract.functions() .check_permissions() - .call(address, document.clone(), &do_call) - .map_err(|e| Error::Internal(e.to_string())) + .call(requester, document.clone(), &do_call) + .map_err(|e| Error::Internal(format!("ACL checker call error: {}", e.to_string()))) }, None => Err(Error::Internal("ACL checker contract is not configured".to_owned())), } @@ -127,18 +129,18 @@ impl CachedContract { impl DummyAclStorage { /// Prohibit given requestor access to given documents #[cfg(test)] - pub fn prohibit(&self, public: Public, document: ServerKeyId) { + pub fn prohibit(&self, requester: Address, document: ServerKeyId) { self.prohibited.write() - .entry(public) + .entry(requester) .or_insert_with(Default::default) .insert(document); } } impl AclStorage for DummyAclStorage { - fn check(&self, public: &Public, document: &ServerKeyId) -> Result { + fn check(&self, requester: Address, document: &ServerKeyId) -> Result { Ok(self.prohibited.read() - .get(public) + .get(&requester) .map(|docs| !docs.contains(document)) .unwrap_or(true)) } diff --git a/dapps/ui/src/lib.rs b/secret_store/src/helpers.rs similarity index 51% rename from dapps/ui/src/lib.rs rename to secret_store/src/helpers.rs index aa1c86736668a09d325f1f91462c171a959ce265..3bc49116e0da487307b9d28c84dc0900c700c5aa 100644 --- a/dapps/ui/src/lib.rs +++ b/secret_store/src/helpers.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -14,33 +14,16 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +use ethcore::client::{Client, BlockChainClient, BlockId}; +use ethereum_types::H256; -#[cfg(feature = "parity-ui-dev")] -mod inner { - extern crate parity_ui_dev; +// TODO: Instead of a constant, make this based on consensus finality. +/// Number of confirmations required before request can be processed. +pub const REQUEST_CONFIRMATIONS_REQUIRED: u64 = 3; - pub use self::parity_ui_dev::*; +/// Get hash of the last block with at least n confirmations. +pub fn get_confirmed_block_hash(client: &Client, confirmations: u64) -> Option { + client.block_number(BlockId::Latest) + .map(|b| b.saturating_sub(confirmations)) + .and_then(|b| client.block_hash(BlockId::Number(b))) } - -#[cfg(feature = "parity-ui-precompiled")] -mod inner { - extern crate parity_ui_precompiled; - - pub use self::parity_ui_precompiled::*; -} - -#[cfg(feature = "parity-ui-old-dev")] -pub mod old { - extern crate parity_ui_old_dev; - - pub use self::parity_ui_old_dev::*; -} - -#[cfg(feature = "parity-ui-old-precompiled")] -pub mod old { - extern crate parity_ui_old_precompiled; - - pub use self::parity_ui_old_precompiled::*; -} - -pub use self::inner::*; diff --git a/secret_store/src/key_server.rs b/secret_store/src/key_server.rs index 181df0caaf65cec360362218fdefdd9e7ac8c725..3b24199776323649974afe5e56fc4312478f56c5 100644 --- a/secret_store/src/key_server.rs +++ b/secret_store/src/key_server.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -21,14 +21,14 @@ use std::sync::mpsc; use futures::{self, Future}; use parking_lot::Mutex; use tokio_core::reactor::Core; -use ethcrypto; -use ethkey; +use crypto::DEFAULT_MAC; +use ethkey::crypto; use super::acl_storage::AclStorage; use super::key_storage::KeyStorage; use super::key_server_set::KeyServerSet; use key_server_cluster::{math, ClusterCore}; use traits::{AdminSessionsServer, ServerKeyGenerator, DocumentKeyServer, MessageSigner, KeyServer, NodeKeyPair}; -use types::all::{Error, Public, RequestSignature, ServerKeyId, EncryptedDocumentKey, EncryptedDocumentKeyShadow, +use types::{Error, Public, RequestSignature, Requester, ServerKeyId, EncryptedDocumentKey, EncryptedDocumentKeyShadow, ClusterConfiguration, MessageHash, EncryptedMessageSignature, NodeId}; use key_server_cluster::{ClusterClient, ClusterConfiguration as NetClusterConfiguration}; @@ -71,78 +71,80 @@ impl AdminSessionsServer for KeyServerImpl { } impl ServerKeyGenerator for KeyServerImpl { - fn generate_key(&self, key_id: &ServerKeyId, signature: &RequestSignature, threshold: usize) -> Result { + fn generate_key(&self, key_id: &ServerKeyId, author: &Requester, threshold: usize) -> Result { // recover requestor' public key from signature - let public = ethkey::recover(signature, key_id) - .map_err(|_| Error::BadSignature)?; + let address = author.address(key_id).map_err(Error::InsufficientRequesterData)?; // generate server key - let generation_session = self.data.lock().cluster.new_generation_session(key_id.clone(), public, threshold)?; - generation_session.wait(None).map_err(Into::into) + let generation_session = self.data.lock().cluster.new_generation_session(key_id.clone(), None, address, threshold)?; + generation_session.wait(None) + .expect("when wait is called without timeout it always returns Some; qed") + .map_err(Into::into) } } impl DocumentKeyServer for KeyServerImpl { - fn store_document_key(&self, key_id: &ServerKeyId, signature: &RequestSignature, common_point: Public, encrypted_document_key: Public) -> Result<(), Error> { + fn store_document_key(&self, key_id: &ServerKeyId, author: &Requester, common_point: Public, encrypted_document_key: Public) -> Result<(), Error> { // store encrypted key let encryption_session = self.data.lock().cluster.new_encryption_session(key_id.clone(), - signature.clone().into(), common_point, encrypted_document_key)?; + author.clone(), common_point, encrypted_document_key)?; encryption_session.wait(None).map_err(Into::into) } - fn generate_document_key(&self, key_id: &ServerKeyId, signature: &RequestSignature, threshold: usize) -> Result { + fn generate_document_key(&self, key_id: &ServerKeyId, author: &Requester, threshold: usize) -> Result { // recover requestor' public key from signature - let public = ethkey::recover(signature, key_id) - .map_err(|_| Error::BadSignature)?; + let public = author.public(key_id).map_err(Error::InsufficientRequesterData)?; // generate server key - let server_key = self.generate_key(key_id, signature, threshold)?; + let server_key = self.generate_key(key_id, author, threshold)?; // generate random document key let document_key = math::generate_random_point()?; let encrypted_document_key = math::encrypt_secret(&document_key, &server_key)?; // store document key in the storage - self.store_document_key(key_id, signature, encrypted_document_key.common_point, encrypted_document_key.encrypted_point)?; + self.store_document_key(key_id, author, encrypted_document_key.common_point, encrypted_document_key.encrypted_point)?; // encrypt document key with requestor public key - let document_key = ethcrypto::ecies::encrypt(&public, ðcrypto::DEFAULT_MAC, &document_key) + let document_key = crypto::ecies::encrypt(&public, &DEFAULT_MAC, &document_key) .map_err(|err| Error::Internal(format!("Error encrypting document key: {}", err)))?; Ok(document_key) } - fn restore_document_key(&self, key_id: &ServerKeyId, signature: &RequestSignature) -> Result { + fn restore_document_key(&self, key_id: &ServerKeyId, requester: &Requester) -> Result { // recover requestor' public key from signature - let public = ethkey::recover(signature, key_id) - .map_err(|_| Error::BadSignature)?; + let public = requester.public(key_id).map_err(Error::InsufficientRequesterData)?; // decrypt document key let decryption_session = self.data.lock().cluster.new_decryption_session(key_id.clone(), - signature.clone().into(), None, false)?; - let document_key = decryption_session.wait()?.decrypted_secret; + None, requester.clone(), None, false, false)?; + let document_key = decryption_session.wait(None) + .expect("when wait is called without timeout it always returns Some; qed")? + .decrypted_secret; // encrypt document key with requestor public key - let document_key = ethcrypto::ecies::encrypt(&public, ðcrypto::DEFAULT_MAC, &document_key) + let document_key = crypto::ecies::encrypt(&public, &DEFAULT_MAC, &document_key) .map_err(|err| Error::Internal(format!("Error encrypting document key: {}", err)))?; Ok(document_key) } - fn restore_document_key_shadow(&self, key_id: &ServerKeyId, signature: &RequestSignature) -> Result { + fn restore_document_key_shadow(&self, key_id: &ServerKeyId, requester: &Requester) -> Result { let decryption_session = self.data.lock().cluster.new_decryption_session(key_id.clone(), - signature.clone().into(), None, true)?; - decryption_session.wait().map_err(Into::into) + None, requester.clone(), None, true, false)?; + decryption_session.wait(None) + .expect("when wait is called without timeout it always returns Some; qed") + .map_err(Into::into) } } impl MessageSigner for KeyServerImpl { - fn sign_message_schnorr(&self, key_id: &ServerKeyId, signature: &RequestSignature, message: MessageHash) -> Result { + fn sign_message_schnorr(&self, key_id: &ServerKeyId, requester: &Requester, message: MessageHash) -> Result { // recover requestor' public key from signature - let public = ethkey::recover(signature, key_id) - .map_err(|_| Error::BadSignature)?; + let public = requester.public(key_id).map_err(Error::InsufficientRequesterData)?; // sign message let signing_session = self.data.lock().cluster.new_schnorr_signing_session(key_id.clone(), - signature.clone().into(), None, message)?; + requester.clone().into(), None, message)?; let message_signature = signing_session.wait()?; // compose two message signature components into single one @@ -151,23 +153,22 @@ impl MessageSigner for KeyServerImpl { combined_signature[32..].clone_from_slice(&**message_signature.1); // encrypt combined signature with requestor public key - let message_signature = ethcrypto::ecies::encrypt(&public, ðcrypto::DEFAULT_MAC, &combined_signature) + let message_signature = crypto::ecies::encrypt(&public, &DEFAULT_MAC, &combined_signature) .map_err(|err| Error::Internal(format!("Error encrypting message signature: {}", err)))?; Ok(message_signature) } - fn sign_message_ecdsa(&self, key_id: &ServerKeyId, signature: &RequestSignature, message: MessageHash) -> Result { + fn sign_message_ecdsa(&self, key_id: &ServerKeyId, requester: &Requester, message: MessageHash) -> Result { // recover requestor' public key from signature - let public = ethkey::recover(signature, key_id) - .map_err(|_| Error::BadSignature)?; + let public = requester.public(key_id).map_err(Error::InsufficientRequesterData)?; // sign message let signing_session = self.data.lock().cluster.new_ecdsa_signing_session(key_id.clone(), - signature.clone().into(), None, message)?; + requester.clone().into(), None, message)?; let message_signature = signing_session.wait()?; // encrypt combined signature with requestor public key - let message_signature = ethcrypto::ecies::encrypt(&public, ðcrypto::DEFAULT_MAC, &*message_signature) + let message_signature = crypto::ecies::encrypt(&public, &DEFAULT_MAC, &*message_signature) .map_err(|err| Error::Internal(format!("Error encrypting message signature: {}", err)))?; Ok(message_signature) } @@ -177,7 +178,7 @@ impl KeyServerCore { pub fn new(config: &ClusterConfiguration, key_server_set: Arc, self_key_pair: Arc, acl_storage: Arc, key_storage: Arc) -> Result { let config = NetClusterConfiguration { threads: config.threads, - self_key_pair: self_key_pair, + self_key_pair: self_key_pair.clone(), listen_address: (config.listener_address.address.clone(), config.listener_address.port), key_server_set: key_server_set, allow_connecting_to_higher_nodes: config.allow_connecting_to_higher_nodes, @@ -189,7 +190,7 @@ impl KeyServerCore { let (stop, stopped) = futures::oneshot(); let (tx, rx) = mpsc::channel(); - let handle = thread::spawn(move || { + let handle = thread::Builder::new().name("KeyServerLoop".into()).spawn(move || { let mut el = match Core::new() { Ok(el) => el, Err(e) => { @@ -202,7 +203,9 @@ impl KeyServerCore { let cluster_client = cluster.and_then(|c| c.run().map(|_| c.client())); tx.send(cluster_client.map_err(Into::into)).expect("Rx is blocking upper thread."); let _ = el.run(futures::empty().select(stopped)); - }); + + trace!(target: "secretstore_net", "{}: KeyServerLoop thread stopped", self_key_pair.public()); + }).map_err(|e| Error::Internal(format!("{}", e)))?; let cluster = rx.recv().map_err(|e| Error::Internal(format!("error initializing event loop: {}", e)))??; Ok(KeyServerCore { @@ -225,26 +228,25 @@ pub mod tests { use std::collections::BTreeSet; use std::time; use std::sync::Arc; - use std::sync::atomic::{AtomicUsize, Ordering}; use std::net::SocketAddr; use std::collections::BTreeMap; - use ethcrypto; - use ethkey::{self, Secret, Random, Generator, verify_public}; + use crypto::DEFAULT_MAC; + use ethkey::{self, crypto, Secret, Random, Generator, verify_public}; use acl_storage::DummyAclStorage; + use key_storage::KeyStorage; use key_storage::tests::DummyKeyStorage; use node_key_pair::PlainNodeKeyPair; use key_server_set::tests::MapKeyServerSet; use key_server_cluster::math; use ethereum_types::{H256, H520}; - use types::all::{Error, Public, ClusterConfiguration, NodeAddress, RequestSignature, ServerKeyId, - EncryptedDocumentKey, EncryptedDocumentKeyShadow, MessageHash, EncryptedMessageSignature, NodeId}; + use types::{Error, Public, ClusterConfiguration, NodeAddress, RequestSignature, ServerKeyId, + EncryptedDocumentKey, EncryptedDocumentKeyShadow, MessageHash, EncryptedMessageSignature, + Requester, NodeId}; use traits::{AdminSessionsServer, ServerKeyGenerator, DocumentKeyServer, MessageSigner, KeyServer}; use super::KeyServerImpl; #[derive(Default)] - pub struct DummyKeyServer { - pub generation_requests_count: AtomicUsize, - } + pub struct DummyKeyServer; impl KeyServer for DummyKeyServer {} @@ -255,41 +257,40 @@ pub mod tests { } impl ServerKeyGenerator for DummyKeyServer { - fn generate_key(&self, _key_id: &ServerKeyId, _signature: &RequestSignature, _threshold: usize) -> Result { - self.generation_requests_count.fetch_add(1, Ordering::Relaxed); - Err(Error::Internal("test error".into())) + fn generate_key(&self, _key_id: &ServerKeyId, _author: &Requester, _threshold: usize) -> Result { + unimplemented!("test-only") } } impl DocumentKeyServer for DummyKeyServer { - fn store_document_key(&self, _key_id: &ServerKeyId, _signature: &RequestSignature, _common_point: Public, _encrypted_document_key: Public) -> Result<(), Error> { + fn store_document_key(&self, _key_id: &ServerKeyId, _author: &Requester, _common_point: Public, _encrypted_document_key: Public) -> Result<(), Error> { unimplemented!("test-only") } - fn generate_document_key(&self, _key_id: &ServerKeyId, _signature: &RequestSignature, _threshold: usize) -> Result { + fn generate_document_key(&self, _key_id: &ServerKeyId, _author: &Requester, _threshold: usize) -> Result { unimplemented!("test-only") } - fn restore_document_key(&self, _key_id: &ServerKeyId, _signature: &RequestSignature) -> Result { + fn restore_document_key(&self, _key_id: &ServerKeyId, _requester: &Requester) -> Result { unimplemented!("test-only") } - fn restore_document_key_shadow(&self, _key_id: &ServerKeyId, _signature: &RequestSignature) -> Result { + fn restore_document_key_shadow(&self, _key_id: &ServerKeyId, _requester: &Requester) -> Result { unimplemented!("test-only") } } impl MessageSigner for DummyKeyServer { - fn sign_message_schnorr(&self, _key_id: &ServerKeyId, _signature: &RequestSignature, _message: MessageHash) -> Result { + fn sign_message_schnorr(&self, _key_id: &ServerKeyId, _requester: &Requester, _message: MessageHash) -> Result { unimplemented!("test-only") } - fn sign_message_ecdsa(&self, _key_id: &ServerKeyId, _signature: &RequestSignature, _message: MessageHash) -> Result { + fn sign_message_ecdsa(&self, _key_id: &ServerKeyId, _requester: &Requester, _message: MessageHash) -> Result { unimplemented!("test-only") } } - fn make_key_servers(start_port: u16, num_nodes: usize) -> Vec { + fn make_key_servers(start_port: u16, num_nodes: usize) -> (Vec, Vec>) { let key_pairs: Vec<_> = (0..num_nodes).map(|_| Random.generate().unwrap()).collect(); let configs: Vec<_> = (0..num_nodes).map(|i| ClusterConfiguration { threads: 1, @@ -302,6 +303,7 @@ pub mod tests { address: "127.0.0.1".into(), port: start_port + (j as u16), })).collect(), + key_server_set_contract_address: None, allow_connecting_to_higher_nodes: false, admin_public: None, auto_migrate_enabled: false, @@ -309,11 +311,12 @@ pub mod tests { let key_servers_set: BTreeMap = configs[0].nodes.iter() .map(|(k, a)| (k.clone(), format!("{}:{}", a.address, a.port).parse().unwrap())) .collect(); + let key_storages = (0..num_nodes).map(|_| Arc::new(DummyKeyStorage::default())).collect::>(); let key_servers: Vec<_> = configs.into_iter().enumerate().map(|(i, cfg)| - KeyServerImpl::new(&cfg, Arc::new(MapKeyServerSet::new(key_servers_set.clone())), + KeyServerImpl::new(&cfg, Arc::new(MapKeyServerSet::new(false, key_servers_set.clone())), Arc::new(PlainNodeKeyPair::new(key_pairs[i].clone())), Arc::new(DummyAclStorage::default()), - Arc::new(DummyKeyStorage::default())).unwrap() + key_storages[i].clone()).unwrap() ).collect(); // wait until connections are established. It is fast => do not bother with events here @@ -343,26 +346,26 @@ pub mod tests { } } - key_servers + (key_servers, key_storages) } #[test] fn document_key_generation_and_retrievement_works_over_network_with_single_node() { //::logger::init_log(); - let key_servers = make_key_servers(6070, 1); + let (key_servers, _) = make_key_servers(6070, 1); // generate document key let threshold = 0; let document = Random.generate().unwrap().secret().clone(); let secret = Random.generate().unwrap().secret().clone(); let signature = ethkey::sign(&secret, &document).unwrap(); - let generated_key = key_servers[0].generate_document_key(&document, &signature, threshold).unwrap(); - let generated_key = ethcrypto::ecies::decrypt(&secret, ðcrypto::DEFAULT_MAC, &generated_key).unwrap(); + let generated_key = key_servers[0].generate_document_key(&document, &signature.clone().into(), threshold).unwrap(); + let generated_key = crypto::ecies::decrypt(&secret, &DEFAULT_MAC, &generated_key).unwrap(); // now let's try to retrieve key back for key_server in key_servers.iter() { - let retrieved_key = key_server.restore_document_key(&document, &signature).unwrap(); - let retrieved_key = ethcrypto::ecies::decrypt(&secret, ðcrypto::DEFAULT_MAC, &retrieved_key).unwrap(); + let retrieved_key = key_server.restore_document_key(&document, &signature.clone().into()).unwrap(); + let retrieved_key = crypto::ecies::decrypt(&secret, &DEFAULT_MAC, &retrieved_key).unwrap(); assert_eq!(retrieved_key, generated_key); } } @@ -370,7 +373,7 @@ pub mod tests { #[test] fn document_key_generation_and_retrievement_works_over_network_with_3_nodes() { //::logger::init_log(); - let key_servers = make_key_servers(6080, 3); + let (key_servers, key_storages) = make_key_servers(6080, 3); let test_cases = [0, 1, 2]; for threshold in &test_cases { @@ -378,14 +381,18 @@ pub mod tests { let document = Random.generate().unwrap().secret().clone(); let secret = Random.generate().unwrap().secret().clone(); let signature = ethkey::sign(&secret, &document).unwrap(); - let generated_key = key_servers[0].generate_document_key(&document, &signature, *threshold).unwrap(); - let generated_key = ethcrypto::ecies::decrypt(&secret, ðcrypto::DEFAULT_MAC, &generated_key).unwrap(); + let generated_key = key_servers[0].generate_document_key(&document, &signature.clone().into(), *threshold).unwrap(); + let generated_key = crypto::ecies::decrypt(&secret, &DEFAULT_MAC, &generated_key).unwrap(); // now let's try to retrieve key back - for key_server in key_servers.iter() { - let retrieved_key = key_server.restore_document_key(&document, &signature).unwrap(); - let retrieved_key = ethcrypto::ecies::decrypt(&secret, ðcrypto::DEFAULT_MAC, &retrieved_key).unwrap(); + for (i, key_server) in key_servers.iter().enumerate() { + let retrieved_key = key_server.restore_document_key(&document, &signature.clone().into()).unwrap(); + let retrieved_key = crypto::ecies::decrypt(&secret, &DEFAULT_MAC, &retrieved_key).unwrap(); assert_eq!(retrieved_key, generated_key); + + let key_share = key_storages[i].get(&document).unwrap().unwrap(); + assert!(key_share.common_point.is_some()); + assert!(key_share.encrypted_point.is_some()); } } } @@ -393,7 +400,7 @@ pub mod tests { #[test] fn server_key_generation_and_storing_document_key_works_over_network_with_3_nodes() { //::logger::init_log(); - let key_servers = make_key_servers(6090, 3); + let (key_servers, _) = make_key_servers(6090, 3); let test_cases = [0, 1, 2]; for threshold in &test_cases { @@ -401,19 +408,20 @@ pub mod tests { let server_key_id = Random.generate().unwrap().secret().clone(); let requestor_secret = Random.generate().unwrap().secret().clone(); let signature = ethkey::sign(&requestor_secret, &server_key_id).unwrap(); - let server_public = key_servers[0].generate_key(&server_key_id, &signature, *threshold).unwrap(); + let server_public = key_servers[0].generate_key(&server_key_id, &signature.clone().into(), *threshold).unwrap(); // generate document key (this is done by KS client so that document key is unknown to any KS) let generated_key = Random.generate().unwrap().public().clone(); let encrypted_document_key = math::encrypt_secret(&generated_key, &server_public).unwrap(); // store document key - key_servers[0].store_document_key(&server_key_id, &signature, encrypted_document_key.common_point, encrypted_document_key.encrypted_point).unwrap(); + key_servers[0].store_document_key(&server_key_id, &signature.clone().into(), + encrypted_document_key.common_point, encrypted_document_key.encrypted_point).unwrap(); // now let's try to retrieve key back for key_server in key_servers.iter() { - let retrieved_key = key_server.restore_document_key(&server_key_id, &signature).unwrap(); - let retrieved_key = ethcrypto::ecies::decrypt(&requestor_secret, ðcrypto::DEFAULT_MAC, &retrieved_key).unwrap(); + let retrieved_key = key_server.restore_document_key(&server_key_id, &signature.clone().into()).unwrap(); + let retrieved_key = crypto::ecies::decrypt(&requestor_secret, &DEFAULT_MAC, &retrieved_key).unwrap(); let retrieved_key = Public::from_slice(&retrieved_key); assert_eq!(retrieved_key, generated_key); } @@ -423,7 +431,7 @@ pub mod tests { #[test] fn server_key_generation_and_message_signing_works_over_network_with_3_nodes() { //::logger::init_log(); - let key_servers = make_key_servers(6100, 3); + let (key_servers, _) = make_key_servers(6100, 3); let test_cases = [0, 1, 2]; for threshold in &test_cases { @@ -431,14 +439,14 @@ pub mod tests { let server_key_id = Random.generate().unwrap().secret().clone(); let requestor_secret = Random.generate().unwrap().secret().clone(); let signature = ethkey::sign(&requestor_secret, &server_key_id).unwrap(); - let server_public = key_servers[0].generate_key(&server_key_id, &signature, *threshold).unwrap(); + let server_public = key_servers[0].generate_key(&server_key_id, &signature.clone().into(), *threshold).unwrap(); // sign message let message_hash = H256::from(42); - let combined_signature = key_servers[0].sign_message_schnorr(&server_key_id, &signature, message_hash.clone()).unwrap(); - let combined_signature = ethcrypto::ecies::decrypt(&requestor_secret, ðcrypto::DEFAULT_MAC, &combined_signature).unwrap(); - let signature_c = Secret::from_slice(&combined_signature[..32]); - let signature_s = Secret::from_slice(&combined_signature[32..]); + let combined_signature = key_servers[0].sign_message_schnorr(&server_key_id, &signature.into(), message_hash.clone()).unwrap(); + let combined_signature = crypto::ecies::decrypt(&requestor_secret, &DEFAULT_MAC, &combined_signature).unwrap(); + let signature_c = Secret::from_slice(&combined_signature[..32]).unwrap(); + let signature_s = Secret::from_slice(&combined_signature[32..]).unwrap(); // check signature assert_eq!(math::verify_schnorr_signature(&server_public, &(signature_c, signature_s), &message_hash), Ok(true)); @@ -448,46 +456,46 @@ pub mod tests { #[test] fn decryption_session_is_delegated_when_node_does_not_have_key_share() { //::logger::init_log(); - let key_servers = make_key_servers(6110, 3); + let (key_servers, _) = make_key_servers(6110, 3); // generate document key let threshold = 0; let document = Random.generate().unwrap().secret().clone(); let secret = Random.generate().unwrap().secret().clone(); let signature = ethkey::sign(&secret, &document).unwrap(); - let generated_key = key_servers[0].generate_document_key(&document, &signature, threshold).unwrap(); - let generated_key = ethcrypto::ecies::decrypt(&secret, ðcrypto::DEFAULT_MAC, &generated_key).unwrap(); + let generated_key = key_servers[0].generate_document_key(&document, &signature.clone().into(), threshold).unwrap(); + let generated_key = crypto::ecies::decrypt(&secret, &DEFAULT_MAC, &generated_key).unwrap(); // remove key from node0 key_servers[0].cluster().key_storage().remove(&document).unwrap(); // now let's try to retrieve key back by requesting it from node0, so that session must be delegated - let retrieved_key = key_servers[0].restore_document_key(&document, &signature).unwrap(); - let retrieved_key = ethcrypto::ecies::decrypt(&secret, ðcrypto::DEFAULT_MAC, &retrieved_key).unwrap(); + let retrieved_key = key_servers[0].restore_document_key(&document, &signature.into()).unwrap(); + let retrieved_key = crypto::ecies::decrypt(&secret, &DEFAULT_MAC, &retrieved_key).unwrap(); assert_eq!(retrieved_key, generated_key); } #[test] fn schnorr_signing_session_is_delegated_when_node_does_not_have_key_share() { //::logger::init_log(); - let key_servers = make_key_servers(6114, 3); + let (key_servers, _) = make_key_servers(6114, 3); let threshold = 1; // generate server key let server_key_id = Random.generate().unwrap().secret().clone(); let requestor_secret = Random.generate().unwrap().secret().clone(); let signature = ethkey::sign(&requestor_secret, &server_key_id).unwrap(); - let server_public = key_servers[0].generate_key(&server_key_id, &signature, threshold).unwrap(); + let server_public = key_servers[0].generate_key(&server_key_id, &signature.clone().into(), threshold).unwrap(); // remove key from node0 key_servers[0].cluster().key_storage().remove(&server_key_id).unwrap(); // sign message let message_hash = H256::from(42); - let combined_signature = key_servers[0].sign_message_schnorr(&server_key_id, &signature, message_hash.clone()).unwrap(); - let combined_signature = ethcrypto::ecies::decrypt(&requestor_secret, ðcrypto::DEFAULT_MAC, &combined_signature).unwrap(); - let signature_c = Secret::from_slice(&combined_signature[..32]); - let signature_s = Secret::from_slice(&combined_signature[32..]); + let combined_signature = key_servers[0].sign_message_schnorr(&server_key_id, &signature.into(), message_hash.clone()).unwrap(); + let combined_signature = crypto::ecies::decrypt(&requestor_secret, &DEFAULT_MAC, &combined_signature).unwrap(); + let signature_c = Secret::from_slice(&combined_signature[..32]).unwrap(); + let signature_s = Secret::from_slice(&combined_signature[32..]).unwrap(); // check signature assert_eq!(math::verify_schnorr_signature(&server_public, &(signature_c, signature_s), &message_hash), Ok(true)); @@ -496,22 +504,22 @@ pub mod tests { #[test] fn ecdsa_signing_session_is_delegated_when_node_does_not_have_key_share() { //::logger::init_log(); - let key_servers = make_key_servers(6117, 4); + let (key_servers, _) = make_key_servers(6117, 4); let threshold = 1; // generate server key let server_key_id = Random.generate().unwrap().secret().clone(); let requestor_secret = Random.generate().unwrap().secret().clone(); let signature = ethkey::sign(&requestor_secret, &server_key_id).unwrap(); - let server_public = key_servers[0].generate_key(&server_key_id, &signature, threshold).unwrap(); + let server_public = key_servers[0].generate_key(&server_key_id, &signature.clone().into(), threshold).unwrap(); // remove key from node0 key_servers[0].cluster().key_storage().remove(&server_key_id).unwrap(); // sign message let message_hash = H256::random(); - let signature = key_servers[0].sign_message_ecdsa(&server_key_id, &signature, message_hash.clone()).unwrap(); - let signature = ethcrypto::ecies::decrypt(&requestor_secret, ðcrypto::DEFAULT_MAC, &signature).unwrap(); + let signature = key_servers[0].sign_message_ecdsa(&server_key_id, &signature.into(), message_hash.clone()).unwrap(); + let signature = crypto::ecies::decrypt(&requestor_secret, &DEFAULT_MAC, &signature).unwrap(); let signature: H520 = signature[0..65].into(); // check signature diff --git a/secret_store/src/key_server_cluster/admin_sessions/key_version_negotiation_session.rs b/secret_store/src/key_server_cluster/admin_sessions/key_version_negotiation_session.rs index b7291c25c8c0164292d414ee83ba57ee2d2700e1..4839ac41e8081b3de386058fefab0c92d4235c8b 100644 --- a/secret_store/src/key_server_cluster/admin_sessions/key_version_negotiation_session.rs +++ b/secret_store/src/key_server_cluster/admin_sessions/key_version_negotiation_session.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -16,7 +16,7 @@ use std::sync::Arc; use std::collections::{BTreeSet, BTreeMap}; -use ethereum_types::H256; +use ethereum_types::{Address, H256}; use ethkey::Secret; use parking_lot::{Mutex, Condvar}; use key_server_cluster::{Error, SessionId, NodeId, DocumentKeyShare}; @@ -25,7 +25,8 @@ use key_server_cluster::cluster_sessions::{SessionIdWithSubSession, ClusterSessi use key_server_cluster::decryption_session::SessionImpl as DecryptionSession; use key_server_cluster::signing_session_ecdsa::SessionImpl as EcdsaSigningSession; use key_server_cluster::signing_session_schnorr::SessionImpl as SchnorrSigningSession; -use key_server_cluster::message::{Message, KeyVersionNegotiationMessage, RequestKeyVersions, KeyVersions}; +use key_server_cluster::message::{Message, KeyVersionNegotiationMessage, RequestKeyVersions, + KeyVersions, KeyVersionsError, FailedKeyVersionContinueAction}; use key_server_cluster::admin_sessions::ShareChangeSessionMeta; // TODO [Opt]: change sessions so that versions are sent by chunks. @@ -34,6 +35,8 @@ const VERSIONS_PER_MESSAGE: usize = 32; /// Key version negotiation transport. pub trait SessionTransport { + /// Broadcast message to all nodes. + fn broadcast(&self, message: KeyVersionNegotiationMessage) -> Result<(), Error>; /// Send message to given node. fn send(&self, node: &NodeId, message: KeyVersionNegotiationMessage) -> Result<(), Error>; } @@ -55,14 +58,21 @@ pub struct SessionImpl { /// Action after key version is negotiated. #[derive(Clone)] pub enum ContinueAction { - /// Decryption session + is_shadow_decryption. - Decrypt(Arc, bool), + /// Decryption session + origin + is_shadow_decryption + is_broadcast_decryption. + Decrypt(Arc, Option
, bool, bool), /// Schnorr signing session + message hash. SchnorrSign(Arc, H256), /// ECDSA signing session + message hash. EcdsaSign(Arc, H256), } +/// Failed action after key version is negotiated. +#[derive(Clone, Debug, PartialEq)] +pub enum FailedContinueAction { + /// Decryption origin + requester. + Decrypt(Option
, Address), +} + /// Immutable session data. struct SessionCore { /// Session meta. @@ -92,9 +102,11 @@ struct SessionData { /// { Version => Nodes } pub versions: Option>>, /// Session result. - pub result: Option>, + pub result: Option, Error>>, /// Continue action. pub continue_with: Option, + /// Failed continue action (reported in error message by master node). + pub failed_continue_with: Option, } /// SessionImpl creation parameters @@ -143,6 +155,10 @@ pub struct FastestResultComputer { self_node_id: NodeId, /// Threshold (if known). threshold: Option, + /// Count of all configured key server nodes. + configured_nodes_count: usize, + /// Count of all connected key server nodes. + connected_nodes_count: usize, } /// Selects version with most support, waiting for responses from all nodes. @@ -151,6 +167,7 @@ pub struct LargestSupportResultComputer; impl SessionImpl where T: SessionTransport { /// Create new session. pub fn new(params: SessionParams) -> Self { + let threshold = params.key_share.as_ref().map(|key_share| key_share.threshold); SessionImpl { core: SessionCore { meta: params.meta, @@ -164,10 +181,11 @@ impl SessionImpl where T: SessionTransport { data: Mutex::new(SessionData { state: SessionState::WaitingForInitialization, confirmations: None, - threshold: None, + threshold: threshold, versions: None, result: None, continue_with: None, + failed_continue_with: None, }) } } @@ -179,13 +197,14 @@ impl SessionImpl where T: SessionTransport { /// Return key threshold. pub fn key_threshold(&self) -> Result { - Ok(self.data.lock().threshold.clone().ok_or(Error::InvalidStateForRequest)?) + self.data.lock().threshold.clone() + .ok_or(Error::InvalidStateForRequest) } /// Return result computer reference. pub fn version_holders(&self, version: &H256) -> Result, Error> { Ok(self.data.lock().versions.as_ref().ok_or(Error::InvalidStateForRequest)? - .get(version).ok_or(Error::KeyStorage("key version not found".into()))? + .get(version).ok_or(Error::ServerKeyIsNotFound)? .clone()) } @@ -199,9 +218,15 @@ impl SessionImpl where T: SessionTransport { self.data.lock().continue_with.take() } + /// Take failed continue action. + pub fn take_failed_continue_action(&self) -> Option { + self.data.lock().failed_continue_with.take() + } + /// Wait for session completion. - pub fn wait(&self) -> Result<(H256, NodeId), Error> { + pub fn wait(&self) -> Result, Error> { Self::wait_session(&self.core.completed, &self.data, None, |data| data.result.clone()) + .expect("wait_session returns Some if called without timeout; qed") } /// Initialize session. @@ -235,7 +260,7 @@ impl SessionImpl where T: SessionTransport { // try to complete session Self::try_complete(&self.core, &mut *data); if no_confirmations_required && data.state != SessionState::Finished { - return Err(Error::MissingKeyShare); + return Err(Error::ServerKeyIsNotFound); } else if data.state == SessionState::Finished { return Ok(()); } @@ -265,7 +290,13 @@ impl SessionImpl where T: SessionTransport { &KeyVersionNegotiationMessage::KeyVersions(ref message) => self.on_key_versions(sender, message), &KeyVersionNegotiationMessage::KeyVersionsError(ref message) => { - self.on_session_error(sender, Error::Io(message.error.clone())); + // remember failed continue action + if let Some(FailedKeyVersionContinueAction::Decrypt(Some(ref origin), ref requester)) = message.continue_with { + self.data.lock().failed_continue_with = + Some(FailedContinueAction::Decrypt(Some(origin.clone().into()), requester.clone().into())); + } + + self.on_session_error(sender, message.error.clone()); Ok(()) }, } @@ -304,6 +335,8 @@ impl SessionImpl where T: SessionTransport { // update state data.state = SessionState::Finished; + data.result = Some(Ok(None)); + self.core.completed.notify_all(); Ok(()) } @@ -356,8 +389,47 @@ impl SessionImpl where T: SessionTransport { let confirmations = data.confirmations.as_ref().expect(reason); let versions = data.versions.as_ref().expect(reason); if let Some(result) = core.result_computer.compute_result(data.threshold.clone(), confirmations, versions) { + // when the master node processing decryption service request, it starts with a key version negotiation session + // if the negotiation fails, only master node knows about it + // => if the error is fatal, only the master will know about it and report it to the contract && the request will never be rejected + // => let's broadcast fatal error so that every other node know about it, and, if it trusts to master node + // will report error to the contract + if let (Some(continue_with), Err(error)) = (data.continue_with.as_ref(), result.as_ref()) { + let origin = match *continue_with { + ContinueAction::Decrypt(_, origin, _, _) => origin.clone(), + _ => None, + }; + + let requester = match *continue_with { + ContinueAction::Decrypt(ref session, _, _, _) => session.requester().and_then(|r| r.address(&core.meta.id).ok()), + _ => None, + }; + + if origin.is_some() && requester.is_some() && !error.is_non_fatal() { + let requester = requester.expect("checked in above condition; qed"); + data.failed_continue_with = + Some(FailedContinueAction::Decrypt(origin.clone(), requester.clone())); + + let send_result = core.transport.broadcast(KeyVersionNegotiationMessage::KeyVersionsError(KeyVersionsError { + session: core.meta.id.clone().into(), + sub_session: core.sub_session.clone().into(), + session_nonce: core.nonce, + error: error.clone(), + continue_with: Some(FailedKeyVersionContinueAction::Decrypt( + origin.map(Into::into), + requester.into(), + )), + })); + + if let Err(send_error) = send_result { + warn!(target: "secretstore_net", "{}: failed to broadcast key version negotiation error {}: {}", + core.meta.self_node_id, error, send_error); + } + } + } + data.state = SessionState::Finished; - data.result = Some(result); + data.result = Some(result.map(Some)); core.completed.notify_all(); } } @@ -385,9 +457,9 @@ impl ClusterSession for SessionImpl where T: SessionTransport { data.confirmations.as_mut().expect("checked a line above; qed").clear(); Self::try_complete(&self.core, &mut *data); if data.state != SessionState::Finished { - warn!("{}: key version negotiation session failed with timeout", self.core.meta.self_node_id); + warn!(target: "secretstore_net", "{}: key version negotiation session failed with timeout", self.core.meta.self_node_id); - data.result = Some(Err(Error::ConsensusUnreachable)); + data.result = Some(Err(Error::ConsensusTemporaryUnreachable)); self.core.completed.notify_all(); } } @@ -402,17 +474,22 @@ impl ClusterSession for SessionImpl where T: SessionTransport { if data.confirmations.is_some() { let is_waiting_for_confirmation = data.confirmations.as_mut().expect("checked a line above; qed").remove(node); - if is_waiting_for_confirmation { - Self::try_complete(&self.core, &mut *data); - if data.state != SessionState::Finished { - warn!("{}: key version negotiation session failed because {} connection has timeouted", self.core.meta.self_node_id, node); - - data.state = SessionState::Finished; - data.result = Some(Err(error)); - self.core.completed.notify_all(); - } + if !is_waiting_for_confirmation { + return; + } + + Self::try_complete(&self.core, &mut *data); + if data.state == SessionState::Finished { + return; } } + + warn!(target: "secretstore_net", "{}: key version negotiation session failed because of {} from {}", + self.core.meta.self_node_id, error, node); + + data.state = SessionState::Finished; + data.result = Some(Err(error)); + self.core.completed.notify_all(); } fn on_message(&self, sender: &NodeId, message: &Message) -> Result<(), Error> { @@ -424,17 +501,23 @@ impl ClusterSession for SessionImpl where T: SessionTransport { } impl SessionTransport for IsolatedSessionTransport { + fn broadcast(&self, message: KeyVersionNegotiationMessage) -> Result<(), Error> { + self.cluster.broadcast(Message::KeyVersionNegotiation(message)) + } + fn send(&self, node: &NodeId, message: KeyVersionNegotiationMessage) -> Result<(), Error> { self.cluster.send(node, Message::KeyVersionNegotiation(message)) } } impl FastestResultComputer { - pub fn new(self_node_id: NodeId, key_share: Option<&DocumentKeyShare>) -> Self { + pub fn new(self_node_id: NodeId, key_share: Option<&DocumentKeyShare>, configured_nodes_count: usize, connected_nodes_count: usize) -> Self { let threshold = key_share.map(|ks| ks.threshold); FastestResultComputer { - self_node_id: self_node_id, - threshold: threshold, + self_node_id, + threshold, + configured_nodes_count, + connected_nodes_count, } }} @@ -442,7 +525,7 @@ impl SessionResultComputer for FastestResultComputer { fn compute_result(&self, threshold: Option, confirmations: &BTreeSet, versions: &BTreeMap>) -> Option> { match self.threshold.or(threshold) { // if there's no versions at all && we're not waiting for confirmations anymore - _ if confirmations.is_empty() && versions.is_empty() => Some(Err(Error::MissingKeyShare)), + _ if confirmations.is_empty() && versions.is_empty() => Some(Err(Error::ServerKeyIsNotFound)), // if we have key share on this node Some(threshold) => { // select version this node have, with enough participants @@ -458,7 +541,17 @@ impl SessionResultComputer for FastestResultComputer { .find(|&(_, ref n)| n.len() >= threshold + 1) .map(|(version, nodes)| Ok((version.clone(), nodes.iter().cloned().nth(0) .expect("version is only inserted when there's at least one owner; qed")))) - .unwrap_or(Err(Error::ConsensusUnreachable))), + // if there's no version consensus among all connected nodes + // AND we're connected to ALL configured nodes + // OR there are less than required nodes for key restore + // => this means that we can't restore key with CURRENT configuration => respond with fatal error + // otherwise we could try later, after all nodes are connected + .unwrap_or_else(|| Err(if self.configured_nodes_count == self.connected_nodes_count + || self.configured_nodes_count < threshold + 1 { + Error::ConsensusUnreachable + } else { + Error::ConsensusTemporaryUnreachable + }))), } }, // if we do not have share, then wait for all confirmations @@ -468,7 +561,11 @@ impl SessionResultComputer for FastestResultComputer { .max_by_key(|&(_, ref n)| n.len()) .map(|(version, nodes)| Ok((version.clone(), nodes.iter().cloned().nth(0) .expect("version is only inserted when there's at least one owner; qed")))) - .unwrap_or(Err(Error::ConsensusUnreachable))), + .unwrap_or_else(|| Err(if self.configured_nodes_count == self.connected_nodes_count { + Error::ConsensusUnreachable + } else { + Error::ConsensusTemporaryUnreachable + }))), } } } @@ -479,7 +576,7 @@ impl SessionResultComputer for LargestSupportResultComputer { return None; } if versions.is_empty() { - return Some(Err(Error::MissingKeyShare)); + return Some(Err(Error::ServerKeyIsNotFound)); } versions.iter() @@ -493,20 +590,28 @@ impl SessionResultComputer for LargestSupportResultComputer { mod tests { use std::sync::Arc; use std::collections::{VecDeque, BTreeMap, BTreeSet}; - use key_server_cluster::{NodeId, SessionId, Error, KeyStorage, DummyKeyStorage, DocumentKeyShare, DocumentKeyShareVersion}; + use ethkey::public_to_address; + use key_server_cluster::{NodeId, SessionId, Error, KeyStorage, DummyKeyStorage, + DocumentKeyShare, DocumentKeyShareVersion}; use key_server_cluster::math; use key_server_cluster::cluster::Cluster; use key_server_cluster::cluster::tests::DummyCluster; + use key_server_cluster::cluster_sessions::ClusterSession; use key_server_cluster::admin_sessions::ShareChangeSessionMeta; + use key_server_cluster::decryption_session::create_default_decryption_session; use key_server_cluster::message::{Message, KeyVersionNegotiationMessage, RequestKeyVersions, KeyVersions}; use super::{SessionImpl, SessionTransport, SessionParams, FastestResultComputer, LargestSupportResultComputer, - SessionResultComputer, SessionState}; + SessionResultComputer, SessionState, ContinueAction, FailedContinueAction}; struct DummyTransport { cluster: Arc, } impl SessionTransport for DummyTransport { + fn broadcast(&self, message: KeyVersionNegotiationMessage) -> Result<(), Error> { + self.cluster.broadcast(Message::KeyVersionNegotiation(message)) + } + fn send(&self, node: &NodeId, message: KeyVersionNegotiationMessage) -> Result<(), Error> { self.cluster.send(node, Message::KeyVersionNegotiation(message)) } @@ -551,12 +656,15 @@ mod tests { id: Default::default(), self_node_id: node_id.clone(), master_node_id: master_node_id.clone(), + configured_nodes_count: nodes.len(), + connected_nodes_count: nodes.len(), }, sub_session: sub_sesion.clone(), key_share: key_storage.get(&Default::default()).unwrap(), result_computer: Arc::new(FastestResultComputer::new( node_id.clone(), key_storage.get(&Default::default()).unwrap().as_ref(), + nodes.len(), nodes.len() )), transport: DummyTransport { cluster: cluster, @@ -576,6 +684,27 @@ mod tests { pub fn session(&self, idx: usize) -> &SessionImpl { &self.nodes.values().nth(idx).unwrap().session } + + pub fn take_message(&mut self) -> Option<(NodeId, NodeId, Message)> { + self.nodes.values() + .filter_map(|n| n.cluster.take_message().map(|m| (n.session.meta().self_node_id.clone(), m.0, m.1))) + .nth(0) + .or_else(|| self.queue.pop_front()) + } + + pub fn process_message(&mut self, msg: (NodeId, NodeId, Message)) -> Result<(), Error> { + match msg.2 { + Message::KeyVersionNegotiation(message) => + self.nodes[&msg.1].session.process_message(&msg.0, &message), + _ => panic!("unexpected"), + } + } + + pub fn run(&mut self) { + while let Some((from, to, message)) = self.take_message() { + self.process_message((from, to, message)).unwrap(); + } + } } #[test] @@ -715,6 +844,9 @@ mod tests { ml.session(0).initialize(ml.nodes.keys().cloned().collect()).unwrap(); // we can't be sure that node has given key version because previous ShareAdd session could fail assert!(ml.session(0).data.lock().state != SessionState::Finished); + + // check that upon completion, threshold is known + assert_eq!(ml.session(0).key_threshold(), Ok(1)); } #[test] @@ -722,13 +854,41 @@ mod tests { let computer = FastestResultComputer { self_node_id: Default::default(), threshold: None, + configured_nodes_count: 1, + connected_nodes_count: 1, }; - assert_eq!(computer.compute_result(Some(10), &Default::default(), &Default::default()), Some(Err(Error::MissingKeyShare))); + assert_eq!(computer.compute_result(Some(10), &Default::default(), &Default::default()), Some(Err(Error::ServerKeyIsNotFound))); } #[test] fn largest_computer_returns_missing_share_if_no_versions_returned() { let computer = LargestSupportResultComputer; - assert_eq!(computer.compute_result(Some(10), &Default::default(), &Default::default()), Some(Err(Error::MissingKeyShare))); + assert_eq!(computer.compute_result(Some(10), &Default::default(), &Default::default()), Some(Err(Error::ServerKeyIsNotFound))); + } + + #[test] + fn fatal_error_is_not_broadcasted_if_started_without_origin() { + let mut ml = MessageLoop::empty(3); + ml.session(0).set_continue_action(ContinueAction::Decrypt(create_default_decryption_session(), None, false, false)); + ml.session(0).initialize(ml.nodes.keys().cloned().collect()).unwrap(); + ml.run(); + + assert!(ml.nodes.values().all(|n| n.session.is_finished() && + n.session.take_failed_continue_action().is_none())); + } + + #[test] + fn fatal_error_is_broadcasted_if_started_with_origin() { + let mut ml = MessageLoop::empty(3); + ml.session(0).set_continue_action(ContinueAction::Decrypt(create_default_decryption_session(), Some(1.into()), true, true)); + ml.session(0).initialize(ml.nodes.keys().cloned().collect()).unwrap(); + ml.run(); + + // on all nodes session is completed + assert!(ml.nodes.values().all(|n| n.session.is_finished())); + + // slave nodes have non-empty failed continue action + assert!(ml.nodes.values().skip(1).all(|n| n.session.take_failed_continue_action() + == Some(FailedContinueAction::Decrypt(Some(1.into()), public_to_address(&2.into()))))); } } diff --git a/secret_store/src/key_server_cluster/admin_sessions/mod.rs b/secret_store/src/key_server_cluster/admin_sessions/mod.rs index 01655653a2c5e529e2db861472957410b2154b5a..1fedc1db40433d3ca73a9370ae7f49b045f26250 100644 --- a/secret_store/src/key_server_cluster/admin_sessions/mod.rs +++ b/secret_store/src/key_server_cluster/admin_sessions/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -32,6 +32,10 @@ pub struct ShareChangeSessionMeta { pub master_node_id: NodeId, /// Id of node, on which this session is running. pub self_node_id: NodeId, + /// Count of all configured key server nodes. + pub configured_nodes_count: usize, + /// Count of all connected key server nodes. + pub connected_nodes_count: usize, } impl ShareChangeSessionMeta { @@ -42,6 +46,8 @@ impl ShareChangeSessionMeta { master_node_id: self.master_node_id, self_node_id: self.self_node_id, threshold: all_nodes_set_len.checked_sub(1).ok_or(Error::ConsensusUnreachable)?, + configured_nodes_count: self.configured_nodes_count, + connected_nodes_count: self.connected_nodes_count, }) } } diff --git a/secret_store/src/key_server_cluster/admin_sessions/servers_set_change_session.rs b/secret_store/src/key_server_cluster/admin_sessions/servers_set_change_session.rs index 01f3392375b7c367857819a7dc8490acb5cfa98d..af612c3d9aa48ddaf26bf77f500ad87f7bbb6a0d 100644 --- a/secret_store/src/key_server_cluster/admin_sessions/servers_set_change_session.rs +++ b/secret_store/src/key_server_cluster/admin_sessions/servers_set_change_session.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -221,6 +221,7 @@ impl SessionImpl { /// Wait for session completion. pub fn wait(&self) -> Result<(), Error> { Self::wait_session(&self.core.completed, &self.data, None, |data| data.result.clone()) + .expect("wait_session returns Some if called without timeout; qed") } /// Initialize servers set change session on master node. @@ -248,10 +249,16 @@ impl SessionImpl { })?; consensus_session.initialize(self.core.all_nodes_set.clone())?; - debug_assert!(consensus_session.state() != ConsensusSessionState::ConsensusEstablished); + + let is_finished = consensus_session.state() == ConsensusSessionState::ConsensusEstablished; data.consensus_session = Some(consensus_session); data.new_nodes_set = Some(new_nodes_set); + // this is the case when all other nodes are isolated + if is_finished { + Self::complete_session(&self.core, &mut *data)?; + } + Ok(()) } @@ -281,7 +288,7 @@ impl SessionImpl { &ServersSetChangeMessage::ServersSetChangeShareAddMessage(ref message) => self.on_share_add_message(sender, message), &ServersSetChangeMessage::ServersSetChangeError(ref message) => { - self.on_session_error(sender, Error::Io(message.error.clone())); + self.on_session_error(sender, message.error.clone()); Ok(()) }, &ServersSetChangeMessage::ServersSetChangeCompleted(ref message) => @@ -337,7 +344,7 @@ impl SessionImpl { } let unknown_sessions_job = UnknownSessionsJob::new_on_master(self.core.key_storage.clone(), self.core.meta.self_node_id.clone()); - consensus_session.disseminate_jobs(unknown_sessions_job, self.unknown_sessions_transport(), false) + consensus_session.disseminate_jobs(unknown_sessions_job, self.unknown_sessions_transport(), false).map(|_| ()) } /// When unknown sessions are requested. @@ -415,12 +422,14 @@ impl SessionImpl { match &message.message { &KeyVersionNegotiationMessage::RequestKeyVersions(ref message) if sender == &self.core.meta.master_node_id => { let key_id = message.session.clone().into(); - let key_share = self.core.key_storage.get(&key_id).map_err(|e| Error::KeyStorage(e.into()))?; + let key_share = self.core.key_storage.get(&key_id)?; let negotiation_session = KeyVersionNegotiationSessionImpl::new(KeyVersionNegotiationSessionParams { meta: ShareChangeSessionMeta { id: key_id.clone(), self_node_id: self.core.meta.self_node_id.clone(), master_node_id: sender.clone(), + configured_nodes_count: self.core.meta.configured_nodes_count, + connected_nodes_count: self.core.meta.connected_nodes_count, }, sub_session: message.sub_session.clone().into(), key_share: key_share, @@ -480,6 +489,7 @@ impl SessionImpl { false => { let master_plan = ShareChangeSessionPlan { key_version: message.version.clone().into(), + version_holders: message.version_holders.iter().cloned().map(Into::into).collect(), consensus_group: message.consensus_group.iter().cloned().map(Into::into).collect(), new_nodes_map: message.new_nodes_map.iter().map(|(k, v)| (k.clone().into(), v.clone().map(Into::into))).collect(), }; @@ -491,23 +501,22 @@ impl SessionImpl { // on nodes, holding selected key share version, we could check if master node plan is correct let master_node_id = message.master_node_id.clone().into(); - if let Some(key_share) = self.core.key_storage.get(&key_id).map_err(|e| Error::KeyStorage(e.into()))? { + if let Some(key_share) = self.core.key_storage.get(&key_id)? { let version = message.version.clone().into(); - if let Ok(key_version) = key_share.version(&version) { - let key_share_owners = key_version.id_numbers.keys().cloned().collect(); - let new_nodes_set = data.new_nodes_set.as_ref() - .expect("new_nodes_set is filled during consensus establishing; change sessions are running after this; qed"); - let local_plan = prepare_share_change_session_plan( - &self.core.all_nodes_set, - key_share.threshold, - version, - &master_node_id, - &key_share_owners, - new_nodes_set)?; - - if local_plan.new_nodes_map.keys().collect::>() != master_plan.new_nodes_map.keys().collect::>() { - return Err(Error::InvalidMessage); - } + let key_share_owners = message.version_holders.iter().cloned().map(Into::into).collect(); + let new_nodes_set = data.new_nodes_set.as_ref() + .expect("new_nodes_set is filled during consensus establishing; change sessions are running after this; qed"); + let local_plan = prepare_share_change_session_plan( + &self.core.all_nodes_set, + key_share.threshold, + &key_id, + version, + &master_node_id, + &key_share_owners, + new_nodes_set)?; + + if local_plan.new_nodes_map.keys().collect::>() != master_plan.new_nodes_map.keys().collect::>() { + return Err(Error::InvalidMessage); } } @@ -658,7 +667,7 @@ impl SessionImpl { if !data.new_nodes_set.as_ref() .expect("new_nodes_set is filled during initialization; session is completed after initialization; qed") .contains(&self.core.meta.self_node_id) { - self.core.key_storage.clear().map_err(|e| Error::KeyStorage(e.into()))?; + self.core.key_storage.clear()?; } data.state = SessionState::Finished; @@ -707,6 +716,8 @@ impl SessionImpl { id: key_id, self_node_id: core.meta.self_node_id.clone(), master_node_id: master_node_id, + configured_nodes_count: core.meta.configured_nodes_count, + connected_nodes_count: core.meta.connected_nodes_count, }, cluster: core.cluster.clone(), key_storage: core.key_storage.clone(), @@ -729,12 +740,14 @@ impl SessionImpl { Some(Ok(key_id)) => key_id, }; - let key_share = core.key_storage.get(&key_id).map_err(|e| Error::KeyStorage(e.into()))?; + let key_share = core.key_storage.get(&key_id)?; let negotiation_session = KeyVersionNegotiationSessionImpl::new(KeyVersionNegotiationSessionParams { meta: ShareChangeSessionMeta { id: key_id, self_node_id: core.meta.self_node_id.clone(), master_node_id: core.meta.self_node_id.clone(), + configured_nodes_count: core.meta.configured_nodes_count, + connected_nodes_count: core.meta.connected_nodes_count, }, sub_session: math::generate_random_scalar()?, key_share: key_share, @@ -783,7 +796,9 @@ impl SessionImpl { // get selected version && old nodes set from key negotiation session let negotiation_session = data.negotiation_sessions.remove(&key_id) .expect("share change session is only initialized when negotiation is completed; qed"); - let (selected_version, selected_master) = negotiation_session.wait()?; + let (selected_version, selected_master) = negotiation_session + .wait()? + .expect("initialize_share_change_session is only called on share change master; negotiation session completes with some on master; qed"); let selected_version_holders = negotiation_session.version_holders(&selected_version)?; let selected_version_threshold = negotiation_session.key_threshold()?; @@ -791,7 +806,13 @@ impl SessionImpl { let old_nodes_set = selected_version_holders; let new_nodes_set = data.new_nodes_set.as_ref() .expect("this method is called after consensus estabished; new_nodes_set is a result of consensus session; qed"); - let session_plan = prepare_share_change_session_plan(&core.all_nodes_set, selected_version_threshold, selected_version.clone(), &selected_master, &old_nodes_set, new_nodes_set)?; + let session_plan = prepare_share_change_session_plan(&core.all_nodes_set, + selected_version_threshold, + &key_id, + selected_version.clone(), + &selected_master, + &old_nodes_set, + new_nodes_set)?; if session_plan.is_empty() { return Ok(false); } @@ -804,6 +825,7 @@ impl SessionImpl { session_nonce: core.nonce, key_id: key_id.clone().into(), version: selected_version.into(), + version_holders: old_nodes_set.iter().cloned().map(Into::into).collect(), master_node_id: selected_master.clone().into(), consensus_group: session_plan.consensus_group.iter().cloned().map(Into::into).collect(), new_nodes_map: session_plan.new_nodes_map.iter() @@ -880,7 +902,7 @@ impl SessionImpl { if !data.new_nodes_set.as_ref() .expect("new_nodes_set is filled during initialization; session is completed after initialization; qed") .contains(&core.meta.self_node_id) { - core.key_storage.clear().map_err(|e| Error::KeyStorage(e.into()))?; + core.key_storage.clear()?; } data.state = SessionState::Finished; @@ -928,7 +950,8 @@ impl ClusterSession for SessionImpl { let mut data = self.data.lock(); - warn!("{}: servers set change session failed: {} on {}", self.core.meta.self_node_id, error, node); + warn!(target: "secretstore_net", "{}: servers set change session failed: {} on {}", + self.core.meta.self_node_id, error, node); data.state = SessionState::Finished; data.result = Some(Err(error)); @@ -993,6 +1016,14 @@ impl JobTransport for UnknownSessionsJobTransport { } impl KeyVersionNegotiationTransport for ServersSetChangeKeyVersionNegotiationTransport { + fn broadcast(&self, message: KeyVersionNegotiationMessage) -> Result<(), Error> { + self.cluster.broadcast(Message::ServersSetChange(ServersSetChangeMessage::ShareChangeKeyVersionNegotiation(ShareChangeKeyVersionNegotiation { + session: self.id.clone().into(), + session_nonce: self.nonce, + message: message, + }))) + } + fn send(&self, node: &NodeId, message: KeyVersionNegotiationMessage) -> Result<(), Error> { self.cluster.send(node, Message::ServersSetChange(ServersSetChangeMessage::ShareChangeKeyVersionNegotiation(ShareChangeKeyVersionNegotiation { session: self.id.clone().into(), @@ -1003,9 +1034,9 @@ impl KeyVersionNegotiationTransport for ServersSetChangeKeyVersionNegotiationTra } fn check_nodes_set(all_nodes_set: &BTreeSet, new_nodes_set: &BTreeSet) -> Result<(), Error> { - // all new nodes must be a part of all nodes set + // all_nodes_set is the set of nodes we're currently connected to (and configured for) match new_nodes_set.iter().any(|n| !all_nodes_set.contains(n)) { - true => Err(Error::InvalidNodesConfiguration), + true => Err(Error::NodeDisconnected), false => Ok(()) } } @@ -1056,7 +1087,7 @@ pub mod tests { }).unwrap() } - fn create_node(meta: ShareChangeSessionMeta, admin_public: Public, all_nodes_set: BTreeSet, node: GenerationNode) -> Node { + fn create_node(meta: ShareChangeSessionMeta, admin_public: Public, all_nodes_set: BTreeSet, node: &GenerationNode) -> Node { for n in &all_nodes_set { node.cluster.add_node(n.clone()); } @@ -1064,18 +1095,18 @@ pub mod tests { Node { cluster: node.cluster.clone(), key_storage: node.key_storage.clone(), - session: create_session(meta, node.session.node().clone(), admin_public, all_nodes_set, node.cluster, node.key_storage), + session: create_session(meta, node.session.node().clone(), admin_public, all_nodes_set, node.cluster.clone(), node.key_storage.clone()), } } impl MessageLoop { - pub fn new(gml: GenerationMessageLoop, master_node_id: NodeId, new_nodes_ids: BTreeSet, removed_nodes_ids: BTreeSet, isolated_nodes_ids: BTreeSet) -> Self { + pub fn new(gml: &GenerationMessageLoop, master_node_id: NodeId, original_key_pair: Option, new_nodes_ids: BTreeSet, removed_nodes_ids: BTreeSet, isolated_nodes_ids: BTreeSet) -> Self { // generate admin key pair let admin_key_pair = Random.generate().unwrap(); let admin_public = admin_key_pair.public().clone(); // compute original secret key - let original_key_pair = gml.compute_key_pair(1); + let original_key_pair = original_key_pair.unwrap_or_else(|| gml.compute_key_pair(1)); // all active nodes set let mut all_nodes_set: BTreeSet<_> = gml.nodes.keys() @@ -1096,9 +1127,11 @@ pub mod tests { self_node_id: master_node_id.clone(), master_node_id: master_node_id.clone(), id: SessionId::default(), + configured_nodes_count: all_nodes_set.len(), + connected_nodes_count: all_nodes_set.len(), }; - let old_nodes = gml.nodes.into_iter().map(|n| create_node(meta.clone(), admin_public.clone(), all_nodes_set.clone(), n.1)); + let old_nodes = gml.nodes.iter().map(|n| create_node(meta.clone(), admin_public.clone(), all_nodes_set.clone(), n.1)); let new_nodes = new_nodes_ids.into_iter().map(|new_node_id| { let new_node_cluster = Arc::new(DummyCluster::new(new_node_id.clone())); for node in &all_nodes_set { @@ -1166,7 +1199,7 @@ pub mod tests { pub fn generate_key(threshold: usize, nodes_ids: BTreeSet) -> GenerationMessageLoop { let mut gml = GenerationMessageLoop::with_nodes_ids(nodes_ids); - gml.master().initialize(Default::default(), false, threshold, gml.nodes.keys().cloned().collect::>().into()).unwrap(); + gml.master().initialize(Default::default(), Default::default(), false, threshold, gml.nodes.keys().cloned().collect::>().into()).unwrap(); while let Some((from, to, message)) = gml.take_message() { gml.process_message((from, to, message)).unwrap(); } @@ -1181,7 +1214,7 @@ pub mod tests { // insert 1 node so that it becames 2-of-4 session let nodes_to_add: BTreeSet<_> = (0..1).map(|_| Random.generate().unwrap().public().clone()).collect(); - let mut ml = MessageLoop::new(gml, master_node_id, nodes_to_add, BTreeSet::new(), BTreeSet::new()); + let mut ml = MessageLoop::new(&gml, master_node_id, None, nodes_to_add, BTreeSet::new(), BTreeSet::new()); ml.nodes[&master_node_id].session.initialize(ml.nodes.keys().cloned().collect(), ml.all_set_signature.clone(), ml.new_set_signature.clone()).unwrap(); ml.run(); @@ -1204,7 +1237,7 @@ pub mod tests { // 3) delegated session is returned back to added node let nodes_to_add: BTreeSet<_> = (0..1).map(|_| Random.generate().unwrap().public().clone()).collect(); let master_node_id = nodes_to_add.iter().cloned().nth(0).unwrap(); - let mut ml = MessageLoop::new(gml, master_node_id, nodes_to_add, BTreeSet::new(), BTreeSet::new()); + let mut ml = MessageLoop::new(&gml, master_node_id, None, nodes_to_add, BTreeSet::new(), BTreeSet::new()); ml.nodes[&master_node_id].session.initialize(ml.nodes.keys().cloned().collect(), ml.all_set_signature.clone(), ml.new_set_signature.clone()).unwrap(); ml.run(); @@ -1221,7 +1254,7 @@ pub mod tests { // remove 1 node && insert 1 node so that one share is moved let nodes_to_remove: BTreeSet<_> = gml.nodes.keys().cloned().skip(1).take(1).collect(); let nodes_to_add: BTreeSet<_> = (0..1).map(|_| Random.generate().unwrap().public().clone()).collect(); - let mut ml = MessageLoop::new(gml, master_node_id, nodes_to_add.clone(), nodes_to_remove.clone(), BTreeSet::new()); + let mut ml = MessageLoop::new(&gml, master_node_id, None, nodes_to_add.clone(), nodes_to_remove.clone(), BTreeSet::new()); let new_nodes_set = ml.nodes.keys().cloned().filter(|n| !nodes_to_remove.contains(n)).collect(); ml.nodes[&master_node_id].session.initialize(new_nodes_set, ml.all_set_signature.clone(), ml.new_set_signature.clone()).unwrap(); ml.run(); @@ -1248,7 +1281,7 @@ pub mod tests { // remove 1 node so that session becames 2-of-2 let nodes_to_remove: BTreeSet<_> = gml.nodes.keys().cloned().skip(1).take(1).collect(); let new_nodes_set: BTreeSet<_> = gml.nodes.keys().cloned().filter(|n| !nodes_to_remove.contains(&n)).collect(); - let mut ml = MessageLoop::new(gml, master_node_id, BTreeSet::new(), nodes_to_remove.clone(), BTreeSet::new()); + let mut ml = MessageLoop::new(&gml, master_node_id, None, BTreeSet::new(), nodes_to_remove.clone(), BTreeSet::new()); ml.nodes[&master_node_id].session.initialize(new_nodes_set, ml.all_set_signature.clone(), ml.new_set_signature.clone()).unwrap(); ml.run(); @@ -1274,7 +1307,7 @@ pub mod tests { // remove 1 node so that session becames 2-of-2 let nodes_to_isolate: BTreeSet<_> = gml.nodes.keys().cloned().skip(1).take(1).collect(); let new_nodes_set: BTreeSet<_> = gml.nodes.keys().cloned().filter(|n| !nodes_to_isolate.contains(&n)).collect(); - let mut ml = MessageLoop::new(gml, master_node_id, BTreeSet::new(), BTreeSet::new(), nodes_to_isolate.clone()); + let mut ml = MessageLoop::new(&gml, master_node_id, None, BTreeSet::new(), BTreeSet::new(), nodes_to_isolate.clone()); ml.nodes[&master_node_id].session.initialize(new_nodes_set, ml.all_set_signature.clone(), ml.new_set_signature.clone()).unwrap(); ml.run(); @@ -1290,4 +1323,85 @@ pub mod tests { // check that all sessions have finished assert!(ml.nodes.iter().filter(|&(k, _)| !nodes_to_isolate.contains(k)).all(|(_, v)| v.session.is_finished())); } + + #[test] + fn having_less_than_required_nodes_after_change_does_not_fail_change_session() { + // initial 2-of-3 session + let gml = generate_key(1, generate_nodes_ids(3)); + let master_node_id = gml.nodes.keys().cloned().nth(0).unwrap(); + + // remove 2 nodes so that key becomes irrecoverable (make sure the session is completed, even though key is irrecoverable) + let nodes_to_remove: BTreeSet<_> = gml.nodes.keys().cloned().skip(1).take(2).collect(); + let new_nodes_set: BTreeSet<_> = gml.nodes.keys().cloned().filter(|n| !nodes_to_remove.contains(&n)).collect(); + let mut ml = MessageLoop::new(&gml, master_node_id, None, BTreeSet::new(), nodes_to_remove.clone(), BTreeSet::new()); + ml.nodes[&master_node_id].session.initialize(new_nodes_set, ml.all_set_signature.clone(), ml.new_set_signature.clone()).unwrap(); + ml.run(); + + // check that all removed nodes do not own key share + assert!(ml.nodes.iter().filter(|&(k, _)| nodes_to_remove.contains(k)).all(|(_, v)| v.key_storage.get(&SessionId::default()).unwrap().is_none())); + + // check that all sessions have finished + assert!(ml.nodes.values().all(|n| n.session.is_finished())); + + // and now let's add new node (make sure the session is completed, even though key is still irrecoverable) + // isolated here are not actually isolated, but removed on the previous step + let nodes_to_add: BTreeSet<_> = (0..1).map(|_| Random.generate().unwrap().public().clone()).collect(); + let new_nodes_set: BTreeSet<_> = gml.nodes.keys().cloned().filter(|n| !nodes_to_remove.contains(&n)) + .chain(nodes_to_add.iter().cloned()) + .collect(); + let master_node_id = nodes_to_add.iter().cloned().nth(0).unwrap(); + let mut ml = MessageLoop::new(&gml, master_node_id, Some(ml.original_key_pair.clone()), nodes_to_add.clone(), BTreeSet::new(), nodes_to_remove.clone()); + ml.nodes[&master_node_id].session.initialize(new_nodes_set, ml.all_set_signature.clone(), ml.new_set_signature.clone()).unwrap(); + ml.run(); + + // check that all added nodes do not own key share (there's not enough nodes to run share add session) + assert!(ml.nodes.iter().filter(|&(k, _)| nodes_to_add.contains(k)).all(|(_, v)| v.key_storage.get(&SessionId::default()).unwrap().is_none())); + + // check that all sessions have finished + assert!(ml.nodes.iter().filter(|&(k, _)| !nodes_to_remove.contains(k)).all(|(_, n)| n.session.is_finished())); + } + + #[test] + fn removing_node_from_cluster_of_2_works() { + // initial 2-of-2 session + let gml = generate_key(1, generate_nodes_ids(2)); + let master_node_id = gml.nodes.keys().cloned().nth(0).unwrap(); + + // make 2nd node isolated so that key becomes irrecoverable (make sure the session is completed, even though key is irrecoverable) + let nodes_to_isolate: BTreeSet<_> = gml.nodes.keys().cloned().skip(1).take(1).collect(); + let new_nodes_set: BTreeSet<_> = gml.nodes.keys().cloned().filter(|n| !nodes_to_isolate.contains(&n)).collect(); + let mut ml = MessageLoop::new(&gml, master_node_id, None, BTreeSet::new(), BTreeSet::new(), nodes_to_isolate.clone()); + ml.nodes[&master_node_id].session.initialize(new_nodes_set, ml.all_set_signature.clone(), ml.new_set_signature.clone()).unwrap(); + ml.run(); + + // check that session on master node has completed (session on 2nd node is not even started in network mode) + assert!(ml.nodes.values().take(1).all(|n| n.session.is_finished())); + } + + #[test] + fn adding_node_that_has_lost_its_database_works() { + // initial 2-of-2 session + let gml = generate_key(1, generate_nodes_ids(2)); + let master_node_id = gml.nodes.keys().cloned().nth(0).unwrap(); + + // insert 1 node so that it becames 2-of-3 session + let nodes_to_add: BTreeSet<_> = (0..1).map(|_| Random.generate().unwrap().public().clone()).collect(); + let mut ml = MessageLoop::new(&gml, master_node_id, None, nodes_to_add.clone(), BTreeSet::new(), BTreeSet::new()); + ml.nodes[&master_node_id].session.initialize(ml.nodes.keys().cloned().collect(), ml.all_set_signature.clone(), ml.new_set_signature.clone()).unwrap(); + ml.run(); + + // now let's say new node has lost its db and we're trying to join it again + ml.nodes[nodes_to_add.iter().nth(0).unwrap()].key_storage.clear().unwrap(); + + // this time old nodes have version, where new node is mentioned, but it doesn't report it when negotiating + let mut ml = MessageLoop::new(&gml, master_node_id, None, nodes_to_add, BTreeSet::new(), BTreeSet::new()); + ml.nodes[&master_node_id].session.initialize(ml.nodes.keys().cloned().collect(), ml.all_set_signature.clone(), ml.new_set_signature.clone()).unwrap(); + ml.run(); + + // try to recover secret for every possible combination of nodes && check that secret is the same + check_secret_is_preserved(ml.original_key_pair.clone(), ml.nodes.iter().map(|(k, v)| (k.clone(), v.key_storage.clone())).collect()); + + // check that all sessions have finished + assert!(ml.nodes.values().all(|n| n.session.is_finished())); + } } diff --git a/secret_store/src/key_server_cluster/admin_sessions/sessions_queue.rs b/secret_store/src/key_server_cluster/admin_sessions/sessions_queue.rs index 35adaab68cdafb49e038810453262836fa2b8fce..7657dfc82638901d17de66abcde1263745ef817f 100644 --- a/secret_store/src/key_server_cluster/admin_sessions/sessions_queue.rs +++ b/secret_store/src/key_server_cluster/admin_sessions/sessions_queue.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/secret_store/src/key_server_cluster/admin_sessions/share_add_session.rs b/secret_store/src/key_server_cluster/admin_sessions/share_add_session.rs index 7da73543bea0a323c1e5679e46cc6a330f21b037..51a027dca3865935e03f55f612df7554fc3641f5 100644 --- a/secret_store/src/key_server_cluster/admin_sessions/share_add_session.rs +++ b/secret_store/src/key_server_cluster/admin_sessions/share_add_session.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -39,7 +39,7 @@ pub trait SessionTransport: Clone + JobTransport Result<(), Error>; /// Set data for master node (sent to slave nodes in consensus session initialization message). - fn set_master_data(&mut self, consensus_group: BTreeSet, id_numbers: BTreeMap>); + fn set_master_data(&mut self, consensus_group: BTreeSet, version_holders: BTreeSet, id_numbers: BTreeMap>); } /// Share addition session. @@ -86,6 +86,8 @@ struct SessionData { pub version: Option, /// Consensus session. pub consensus_session: Option>, + /// Holders of key version. + pub version_holders: Option>, /// NewKeyShare (for nodes being added). pub new_key_share: Option, /// Nodes id numbers. @@ -144,6 +146,8 @@ pub struct IsolatedSessionTransport { version: Option, /// Session-level nonce. nonce: u64, + /// Holders of key version. + version_holders: Option>, /// Consensus group. consensus_group: Option>, /// Id numbers of all new nodes. @@ -155,9 +159,7 @@ pub struct IsolatedSessionTransport { impl SessionImpl where T: SessionTransport { /// Create new share addition session. pub fn new(params: SessionParams) -> Result { - let key_share = params.key_storage - .get(¶ms.meta.id) - .map_err(|e| Error::KeyStorage(e.into()))?; + let key_share = params.key_storage.get(¶ms.meta.id)?; Ok(SessionImpl { core: SessionCore { @@ -173,6 +175,7 @@ impl SessionImpl where T: SessionTransport { state: SessionState::ConsensusEstablishing, version: None, consensus_session: None, + version_holders: None, new_key_share: None, id_numbers: None, secret_subshares: None, @@ -182,7 +185,7 @@ impl SessionImpl where T: SessionTransport { } /// Set pre-established consensus data. - pub fn set_consensus_output(&self, version: &H256, consensus_group: BTreeSet, mut new_nodes_map: BTreeMap>) -> Result<(), Error> { + pub fn set_consensus_output(&self, version: &H256, consensus_group: BTreeSet, version_holders: BTreeSet, mut new_nodes_map: BTreeMap>) -> Result<(), Error> { let mut data = self.data.lock(); // check state @@ -193,18 +196,30 @@ impl SessionImpl where T: SessionTransport { // key share version is required on ShareAdd master node if let Some(key_share) = self.core.key_share.as_ref() { if let Ok(key_version) = key_share.version(version) { + let non_isolated_nodes = self.core.transport.nodes(); for (node, id_number) in &key_version.id_numbers { { let external_id_number = new_nodes_map.get(node); match external_id_number { Some(&Some(ref external_id_number)) => { + if !version_holders.contains(node) { + // possible when joining version holder, that has lost its database + // and haven't reported version ownership + continue; + } if external_id_number == id_number { continue; } + return Err(Error::ConsensusUnreachable); }, Some(&None) => (), - None => return Err(Error::ConsensusUnreachable), + None => { + if non_isolated_nodes.contains(node) { + return Err(Error::ConsensusUnreachable) + } + continue; + }, } } @@ -219,7 +234,7 @@ impl SessionImpl where T: SessionTransport { } // check passed consensus data - Self::check_nodes_map(&self.core, version, &consensus_group, &new_nodes_map)?; + Self::check_nodes_map(&self.core, version, &consensus_group, &version_holders, &new_nodes_map)?; // update data data.version = Some(version.clone()); @@ -227,6 +242,7 @@ impl SessionImpl where T: SessionTransport { data.secret_subshares = Some(consensus_group.into_iter() .map(|n| (n, None)) .collect()); + data.version_holders = Some(version_holders); Ok(()) } @@ -257,8 +273,8 @@ impl SessionImpl where T: SessionTransport { let admin_public = self.core.admin_public.as_ref().cloned().ok_or(Error::ConsensusUnreachable)?; // key share version is required on ShareAdd master node - let key_share = self.core.key_share.as_ref().ok_or_else(|| Error::KeyStorage("key share is not found on master node".into()))?; - let key_version = key_share.version(&version).map_err(|e| Error::KeyStorage(e.into()))?; + let key_share = self.core.key_share.as_ref().ok_or_else(|| Error::ServerKeyIsNotFound)?; + let key_version = key_share.version(&version)?; // old nodes set is all non-isolated owners of version holders let non_isolated_nodes = self.core.transport.nodes(); @@ -283,13 +299,14 @@ impl SessionImpl where T: SessionTransport { .take(key_share.threshold) .cloned()) .collect(); + let version_holders = &old_nodes_set; // now check nodes map - Self::check_nodes_map(&self.core, &version, &consensus_group, &new_nodes_map)?; + Self::check_nodes_map(&self.core, &version, &consensus_group, version_holders, &new_nodes_map)?; // prepare consensus session transport let mut consensus_transport = self.core.transport.clone(); - consensus_transport.set_master_data(consensus_group.clone(), new_nodes_map.clone()); + consensus_transport.set_master_data(consensus_group.clone(), version_holders.clone(), new_nodes_map.clone()); // create && initialize consensus session let mut consensus_session = ConsensusSession::new(ConsensusSessionParams { @@ -308,6 +325,7 @@ impl SessionImpl where T: SessionTransport { data.consensus_session = Some(consensus_session); data.id_numbers = Some(new_nodes_map); data.secret_subshares = Some(consensus_group.into_iter().map(|n| (n, None)).collect()); + data.version_holders = Some(version_holders.clone()); Ok(()) } @@ -326,7 +344,7 @@ impl SessionImpl where T: SessionTransport { &ShareAddMessage::NewKeysDissemination(ref message) => self.on_new_keys_dissemination(sender, message), &ShareAddMessage::ShareAddError(ref message) => { - self.on_session_error(sender, Error::Io(message.error.clone().into())); + self.on_session_error(sender, message.error.clone()); Ok(()) }, } @@ -353,16 +371,17 @@ impl SessionImpl where T: SessionTransport { }; // process consensus message - let (is_establishing_consensus, is_consensus_established, version, new_nodes_map, consensus_group) = { + let (is_establishing_consensus, is_consensus_established, version, new_nodes_map, consensus_group, version_holders) = { let consensus_session = data.consensus_session.as_mut().ok_or(Error::InvalidMessage)?; let is_establishing_consensus = consensus_session.state() == ConsensusSessionState::EstablishingConsensus; - let (version, new_nodes_map, consensus_group) = match &message.message { + let (version, new_nodes_map, consensus_group, version_holders) = match &message.message { &ConsensusMessageOfShareAdd::InitializeConsensusSession(ref message) => { consensus_session.on_consensus_partial_request(sender, ServersSetChangeAccessRequest::from(message))?; let version = message.version.clone().into(); let consensus_group = message.consensus_group.iter().cloned().map(Into::into).collect(); + let version_holders = message.version_holders.iter().cloned().map(Into::into).collect(); let new_nodes_map: BTreeMap<_, _> = message.new_nodes_map.iter() .map(|(n, nn)| (n.clone().into(), Some(nn.clone().into()))) .collect(); @@ -373,13 +392,13 @@ impl SessionImpl where T: SessionTransport { } // check old set of nodes - Self::check_nodes_map(&self.core, &version, &consensus_group, &new_nodes_map)?; + Self::check_nodes_map(&self.core, &version, &consensus_group, &version_holders, &new_nodes_map)?; - (Some(version), Some(new_nodes_map), Some(consensus_group)) + (Some(version), Some(new_nodes_map), Some(consensus_group), Some(version_holders)) }, &ConsensusMessageOfShareAdd::ConfirmConsensusInitialization(ref message) => { consensus_session.on_consensus_partial_response(sender, message.is_confirmed)?; - (None, None, None) + (None, None, None, None) }, }; @@ -389,6 +408,7 @@ impl SessionImpl where T: SessionTransport { version, new_nodes_map, consensus_group, + version_holders, ) }; @@ -402,6 +422,9 @@ impl SessionImpl where T: SessionTransport { if let Some(consensus_group) = consensus_group { data.secret_subshares = Some(consensus_group.into_iter().map(|n| (n, None)).collect()); } + if let Some(version_holders) = version_holders { + data.version_holders = Some(version_holders); + } // if consensus is stablished, proceed if !is_establishing_consensus || !is_consensus_established || self.core.meta.self_node_id != self.core.meta.master_node_id { @@ -453,7 +476,7 @@ impl SessionImpl where T: SessionTransport { }); let id_numbers = data.id_numbers.as_mut() - .expect("common key share data is expected after initialization; id_numers are filled during initialization; qed"); + .expect("common key share data is expected after initialization; id_numbers are filled during initialization; qed"); for (node, id_number) in &message.id_numbers { let id_number: Secret = id_number.clone().into(); { @@ -521,7 +544,7 @@ impl SessionImpl where T: SessionTransport { } /// Check nodes map. - fn check_nodes_map(core: &SessionCore, version: &H256, consensus_group: &BTreeSet, new_nodes_map: &BTreeMap>) -> Result<(), Error> { + fn check_nodes_map(core: &SessionCore, version: &H256, consensus_group: &BTreeSet, version_holders: &BTreeSet, new_nodes_map: &BTreeMap>) -> Result<(), Error> { // check if this node has given version let has_this_version = match core.key_share.as_ref() { Some(key_share) => key_share.version(version).is_ok(), @@ -548,7 +571,7 @@ impl SessionImpl where T: SessionTransport { } // there must be at least one new node in new_nodes_map - if key_version.id_numbers.len() >= new_nodes_map.len() { + if key_version.id_numbers.keys().filter(|n| non_isolated_nodes.contains(n) && version_holders.contains(n)).count() >= new_nodes_map.len() { return Err(Error::ConsensusUnreachable); } }, @@ -609,6 +632,8 @@ impl SessionImpl where T: SessionTransport { let explanation = "disseminate_common_share_data is only called on master node; master node has specified version of the key; qed"; let old_key_share = core.key_share.as_ref().expect(explanation); let old_key_version = old_key_share.version(data.version.as_ref().expect(explanation)).expect(explanation); + let version_holders = data.version_holders.as_ref() + .expect("disseminate_common_share_data is only called on master node; version holders is created during initialization on master node; qed"); let consensus_group = data.secret_subshares.as_ref() .expect("disseminate_common_share_data is only called on master node; consensus group is created during initialization on master node; qed"); let nodes = data.id_numbers.as_ref() @@ -624,7 +649,9 @@ impl SessionImpl where T: SessionTransport { joint_public: old_key_share.public.clone().into(), common_point: old_key_share.common_point.clone().map(Into::into), encrypted_point: old_key_share.encrypted_point.clone().map(Into::into), - id_numbers: old_key_version.id_numbers.iter().map(|(k, v)| (k.clone().into(), v.clone().into())).collect(), + id_numbers: old_key_version.id_numbers.iter() + .filter(|&(k, _)| version_holders.contains(k)) + .map(|(k, v)| (k.clone().into(), v.clone().into())).collect(), }))?; } @@ -714,10 +741,10 @@ impl SessionImpl where T: SessionTransport { // save encrypted data to the key storage data.state = SessionState::Finished; if core.key_share.is_some() { - core.key_storage.update(core.meta.id.clone(), refreshed_key_share.clone()) + core.key_storage.update(core.meta.id.clone(), refreshed_key_share.clone())?; } else { - core.key_storage.insert(core.meta.id.clone(), refreshed_key_share.clone()) - }.map_err(|e| Error::KeyStorage(e.into()))?; + core.key_storage.insert(core.meta.id.clone(), refreshed_key_share.clone())?; + } // signal session completion data.state = SessionState::Finished; @@ -767,7 +794,8 @@ impl ClusterSession for SessionImpl where T: SessionTransport { let mut data = self.data.lock(); - warn!("{}: share add session failed: {} on {}", self.core.meta.self_node_id, error, node); + warn!(target: "secretstore_net", "{}: share add session failed: {} on {}", + self.core.meta.self_node_id, error, node); data.state = SessionState::Finished; data.result = Some(Err(error)); @@ -790,6 +818,7 @@ impl IsolatedSessionTransport { nonce: nonce, cluster: cluster, id_numbers: None, + version_holders: None, consensus_group: None, } } @@ -808,6 +837,7 @@ impl JobTransport for IsolatedSessionTransport { session_nonce: self.nonce, message: ConsensusMessageOfShareAdd::InitializeConsensusSession(InitializeConsensusSessionOfShareAdd { version: self.version.clone().expect(explanation).into(), + version_holders: self.version_holders.as_ref().expect(explanation).iter().cloned().map(Into::into).collect(), consensus_group: self.consensus_group.as_ref().expect(explanation).iter().cloned().map(Into::into).collect(), old_nodes_set: request.old_servers_set.into_iter().map(Into::into).collect(), new_nodes_map: request.new_servers_set.into_iter() @@ -838,7 +868,8 @@ impl SessionTransport for IsolatedSessionTransport { self.cluster.nodes() } - fn set_master_data(&mut self, consensus_group: BTreeSet, id_numbers: BTreeMap>) { + fn set_master_data(&mut self, consensus_group: BTreeSet, version_holders: BTreeSet, id_numbers: BTreeMap>) { + self.version_holders = Some(version_holders); self.consensus_group = Some(consensus_group); self.id_numbers = Some(id_numbers); } @@ -851,7 +882,7 @@ impl SessionTransport for IsolatedSessionTransport { #[cfg(test)] pub mod tests { use std::sync::Arc; - use std::collections::{VecDeque, BTreeMap, BTreeSet}; + use std::collections::{VecDeque, BTreeMap, BTreeSet, HashSet}; use ethkey::{Random, Generator, Public, KeyPair, Signature, sign}; use ethereum_types::H256; use key_server_cluster::{NodeId, SessionId, Error, KeyStorage, DummyKeyStorage}; @@ -952,6 +983,8 @@ pub mod tests { id: SessionId::default(), self_node_id: NodeId::default(), master_node_id: master_node_id, + configured_nodes_count: new_nodes_set.iter().chain(old_nodes_set.iter()).collect::>().len(), + connected_nodes_count: new_nodes_set.iter().chain(old_nodes_set.iter()).collect::>().len(), }; let new_nodes = new_nodes_set.iter() .filter(|n| !old_nodes_set.contains(&n)) @@ -992,6 +1025,8 @@ pub mod tests { id: SessionId::default(), self_node_id: NodeId::default(), master_node_id: master_node_id, + configured_nodes_count: new_nodes_set.iter().chain(ml.nodes.keys()).collect::>().len(), + connected_nodes_count: new_nodes_set.iter().chain(ml.nodes.keys()).collect::>().len(), }; let old_nodes_set = ml.nodes.keys().cloned().collect(); let nodes = ml.nodes.iter() @@ -1102,7 +1137,7 @@ pub mod tests { assert_eq!(ml.nodes[&master_node_id].session.initialize(Some(ml.version), Some(new_nodes_set), Some(ml.old_set_signature.clone()), Some(ml.new_set_signature.clone()) - ).unwrap_err(), Error::KeyStorage("key share is not found on master node".into())); + ).unwrap_err(), Error::ServerKeyIsNotFound); } #[test] diff --git a/secret_store/src/key_server_cluster/admin_sessions/share_change_session.rs b/secret_store/src/key_server_cluster/admin_sessions/share_change_session.rs index 228d7ba987645ec7df4796bf8d7cdfdb64347419..48cb81c137a006c0ff98e9f7edbae8abbef09f77 100644 --- a/secret_store/src/key_server_cluster/admin_sessions/share_change_session.rs +++ b/secret_store/src/key_server_cluster/admin_sessions/share_change_session.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -18,7 +18,7 @@ use std::sync::Arc; use std::collections::{BTreeSet, BTreeMap}; use ethereum_types::H256; use ethkey::Secret; -use key_server_cluster::{Error, NodeId, SessionId, KeyStorage}; +use key_server_cluster::{Error, NodeId, SessionId, ServerKeyId, KeyStorage}; use key_server_cluster::cluster::Cluster; use key_server_cluster::cluster_sessions::ClusterSession; use key_server_cluster::math; @@ -48,6 +48,8 @@ pub struct ShareChangeSession { key_storage: Arc, /// Key version. key_version: H256, + /// Nodes that have reported version ownership. + version_holders: Option>, /// Consensus group to use in ShareAdd session. consensus_group: Option>, /// Nodes to add shares for. @@ -63,6 +65,8 @@ pub struct ShareChangeSession { pub struct ShareChangeSessionPlan { /// Key version that plan is valid for. pub key_version: H256, + /// Nodes that have reported version ownership. + pub version_holders: BTreeSet, /// Consensus group to use in ShareAdd session. pub consensus_group: BTreeSet, /// Nodes to add shares for. @@ -102,6 +106,7 @@ impl ShareChangeSession { // we can't create sessions right now, because key share is read when session is created, but it can change in previous session let key_version = params.plan.key_version; let consensus_group = if !params.plan.consensus_group.is_empty() { Some(params.plan.consensus_group) } else { None }; + let version_holders = if !params.plan.version_holders.is_empty() { Some(params.plan.version_holders) } else { None }; let new_nodes_map = if !params.plan.new_nodes_map.is_empty() { Some(params.plan.new_nodes_map) } else { None }; debug_assert!(new_nodes_map.is_some()); @@ -113,6 +118,7 @@ impl ShareChangeSession { cluster: params.cluster, key_storage: params.key_storage, key_version: key_version, + version_holders: version_holders, consensus_group: consensus_group, new_nodes_map: new_nodes_map, share_add_session: None, @@ -158,6 +164,7 @@ impl ShareChangeSession { /// Create new share add session. fn create_share_add_session(&mut self) -> Result<(), Error> { let consensus_group = self.consensus_group.take().ok_or(Error::InvalidStateForRequest)?; + let version_holders = self.version_holders.take().ok_or(Error::InvalidStateForRequest)?; let new_nodes_map = self.new_nodes_map.take().ok_or(Error::InvalidStateForRequest)?; let share_add_session = ShareAddSessionImpl::new(ShareAddSessionParams { meta: self.meta.clone(), @@ -166,7 +173,7 @@ impl ShareChangeSession { key_storage: self.key_storage.clone(), admin_public: None, })?; - share_add_session.set_consensus_output(&self.key_version, consensus_group, new_nodes_map)?; + share_add_session.set_consensus_output(&self.key_version, consensus_group, version_holders, new_nodes_map)?; self.share_add_session = Some(share_add_session); Ok(()) } @@ -221,7 +228,7 @@ impl ShareAddSessionTransport for ShareChangeTransport { self.cluster.nodes() } - fn set_master_data(&mut self, _consensus_group: BTreeSet, _id_numbers: BTreeMap>) { + fn set_master_data(&mut self, _consensus_group: BTreeSet, _version_holders: BTreeSet, _id_numbers: BTreeMap>) { unreachable!("only called when establishing consensus; this transport is never used for establishing consensus; qed") } @@ -235,7 +242,25 @@ impl ShareAddSessionTransport for ShareChangeTransport { } /// Prepare share change plan for moving from old `old_key_version_owners` to `new_nodes_set`. -pub fn prepare_share_change_session_plan(cluster_nodes: &BTreeSet, threshold: usize, key_version: H256, master: &NodeId, old_key_version_owners: &BTreeSet, new_nodes_set: &BTreeSet) -> Result { +pub fn prepare_share_change_session_plan(cluster_nodes: &BTreeSet, threshold: usize, key_id: &ServerKeyId, key_version: H256, master: &NodeId, old_key_version_owners: &BTreeSet, new_nodes_set: &BTreeSet) -> Result { + // we can't do anything if there are no enought shares + if old_key_version_owners.len() < threshold + 1 { + warn!("cannot add shares to key {} with threshold {}: only {} shares owners are available", + key_id, threshold, old_key_version_owners.len()); + return Ok(ShareChangeSessionPlan { + key_version: key_version, + version_holders: Default::default(), + consensus_group: Default::default(), + new_nodes_map: Default::default(), + }); + } + + // warn if we're loosing the key + if new_nodes_set.len() < threshold + 1 { + warn!("losing key {} with threshold {}: only {} nodes left after servers set change session", + key_id, threshold, new_nodes_set.len()); + } + // make new nodes map, so that: // all non-isolated old nodes will have their id number preserved // all new nodes will have new id number @@ -262,6 +287,7 @@ pub fn prepare_share_change_session_plan(cluster_nodes: &BTreeSet, thres Ok(ShareChangeSessionPlan { key_version: key_version, + version_holders: old_key_version_owners.clone(), consensus_group: consensus_group, new_nodes_map: new_nodes_map, }) @@ -285,7 +311,8 @@ mod tests { let master = cluster_nodes[0].clone(); let old_key_version_owners = cluster_nodes.iter().cloned().collect(); let new_nodes_set = cluster_nodes.iter().cloned().collect(); - let plan = prepare_share_change_session_plan(&cluster_nodes.iter().cloned().collect(), 1, Default::default(), &master, &old_key_version_owners, &new_nodes_set).unwrap(); + let plan = prepare_share_change_session_plan(&cluster_nodes.iter().cloned().collect(), + 1, &Default::default(), Default::default(), &master, &old_key_version_owners, &new_nodes_set).unwrap(); assert!(plan.is_empty()); } @@ -296,7 +323,8 @@ mod tests { let master = cluster_nodes[0].clone(); let old_key_version_owners = cluster_nodes[0..2].iter().cloned().collect(); let new_nodes_set = cluster_nodes.iter().cloned().collect(); - let plan = prepare_share_change_session_plan(&cluster_nodes.iter().cloned().collect(), 1, Default::default(), &master, &old_key_version_owners, &new_nodes_set).unwrap(); + let plan = prepare_share_change_session_plan(&cluster_nodes.iter().cloned().collect(), + 1, &Default::default(), Default::default(), &master, &old_key_version_owners, &new_nodes_set).unwrap(); assert!(!plan.is_empty()); assert_eq!(old_key_version_owners, plan.consensus_group); diff --git a/secret_store/src/key_server_cluster/client_sessions/decryption_session.rs b/secret_store/src/key_server_cluster/client_sessions/decryption_session.rs index c3946f74691f8cc0d31bc391b90d91509df028a3..9172a03b1a86d81565e18d2d30ead9fe0447c030 100644 --- a/secret_store/src/key_server_cluster/client_sessions/decryption_session.rs +++ b/secret_store/src/key_server_cluster/client_sessions/decryption_session.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -14,10 +14,11 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use std::collections::BTreeSet; +use std::collections::{BTreeSet, BTreeMap}; use std::sync::Arc; +use std::time; use parking_lot::{Mutex, Condvar}; -use ethereum_types::H256; +use ethereum_types::{Address, H256}; use ethkey::Secret; use key_server_cluster::{Error, AclStorage, DocumentKeyShare, NodeId, SessionId, Requester, EncryptedDocumentKeyShadow, SessionMeta}; @@ -26,7 +27,7 @@ use key_server_cluster::cluster_sessions::{SessionIdWithSubSession, ClusterSessi use key_server_cluster::message::{Message, DecryptionMessage, DecryptionConsensusMessage, RequestPartialDecryption, PartialDecryption, DecryptionSessionError, DecryptionSessionCompleted, ConsensusMessage, InitializeConsensusSession, ConfirmConsensusInitialization, DecryptionSessionDelegation, DecryptionSessionDelegationCompleted}; -use key_server_cluster::jobs::job_session::{JobSession, JobTransport}; +use key_server_cluster::jobs::job_session::{JobSession, JobSessionState, JobTransport}; use key_server_cluster::jobs::key_access_job::KeyAccessJob; use key_server_cluster::jobs::decryption_job::{PartialDecryptionRequest, PartialDecryptionResponse, DecryptionJob}; use key_server_cluster::jobs::consensus_session::{ConsensusSessionParams, ConsensusSessionState, ConsensusSession}; @@ -71,6 +72,8 @@ type BroadcastDecryptionJobSession = JobSession, + /// Session origin (if any). + pub origin: Option
, /// Consensus-based decryption session. pub consensus_session: DecryptionConsensusSession, /// Broadcast decryption job. @@ -110,6 +113,8 @@ struct DecryptionConsensusTransport { access_key: Secret, /// Session-level nonce. nonce: u64, + /// Session origin (if any). + origin: Option
, /// Selected key version (on master node). version: Option, /// Cluster. @@ -149,7 +154,7 @@ impl SessionImpl { if let Some(key_share) = params.key_share.as_ref() { // encrypted data must be set if key_share.common_point.is_none() || key_share.encrypted_point.is_none() { - return Err(Error::NotStartedSessionId); + return Err(Error::DocumentKeyIsNotFound); } } @@ -157,6 +162,7 @@ impl SessionImpl { id: params.meta.id.clone(), access_key: params.access_key.clone(), nonce: params.nonce, + origin: None, version: None, cluster: params.cluster.clone(), }; @@ -180,6 +186,7 @@ impl SessionImpl { }, data: Mutex::new(SessionData { version: None, + origin: None, consensus_session: consensus_session, broadcast_job_session: None, is_shadow_decryption: None, @@ -214,13 +221,42 @@ impl SessionImpl { self.data.lock().result.clone() } + /// Get key requester. + pub fn requester(&self) -> Option { + self.data.lock().consensus_session.consensus_job().executor().requester().cloned() + } + + /// Get session origin. + pub fn origin(&self) -> Option
{ + self.data.lock().origin.clone() + } + /// Wait for session completion. - pub fn wait(&self) -> Result { - Self::wait_session(&self.core.completed, &self.data, None, |data| data.result.clone()) + pub fn wait(&self, timeout: Option) -> Option> { + Self::wait_session(&self.core.completed, &self.data, timeout, |data| data.result.clone()) + } + + /// Get broadcasted shadows. + pub fn broadcast_shadows(&self) -> Option>> { + let data = self.data.lock(); + + if data.result.is_none() || (data.is_broadcast_session, data.is_shadow_decryption) != (Some(true), Some(true)) { + return None; + } + + let proof = "data.is_shadow_decryption is true; decrypt_shadow.is_some() is checked in DecryptionJob::check_partial_response; qed"; + Some(match self.core.meta.master_node_id == self.core.meta.self_node_id { + true => data.consensus_session.computation_job().responses().iter() + .map(|(n, r)| (n.clone(), r.decrypt_shadow.clone().expect(proof))) + .collect(), + false => data.broadcast_job_session.as_ref().expect("session completed; is_shadow_decryption == true; we're on non-master node; qed").responses().iter() + .map(|(n, r)| (n.clone(), r.decrypt_shadow.clone().expect(proof))) + .collect(), + }) } /// Delegate session to other node. - pub fn delegate(&self, master: NodeId, version: H256, is_shadow_decryption: bool, is_broadcast_session: bool) -> Result<(), Error> { + pub fn delegate(&self, master: NodeId, origin: Option
, version: H256, is_shadow_decryption: bool, is_broadcast_session: bool) -> Result<(), Error> { if self.core.meta.master_node_id != self.core.meta.self_node_id { return Err(Error::InvalidStateForRequest); } @@ -235,6 +271,7 @@ impl SessionImpl { session: self.core.meta.id.clone().into(), sub_session: self.core.access_key.clone().into(), session_nonce: self.core.nonce, + origin: origin.map(Into::into), requester: data.consensus_session.consensus_job().executor().requester() .expect("signature is passed to master node on creation; session can be delegated from master node only; qed") .clone().into(), @@ -247,13 +284,13 @@ impl SessionImpl { } /// Initialize decryption session on master node. - pub fn initialize(&self, version: H256, is_shadow_decryption: bool, is_broadcast_session: bool) -> Result<(), Error> { + pub fn initialize(&self, origin: Option
, version: H256, is_shadow_decryption: bool, is_broadcast_session: bool) -> Result<(), Error> { debug_assert_eq!(self.core.meta.self_node_id, self.core.meta.master_node_id); // check if version exists let key_version = match self.core.key_share.as_ref() { None => return Err(Error::InvalidMessage), - Some(key_share) => key_share.version(&version).map_err(|e| Error::KeyStorage(e.into()))?, + Some(key_share) => key_share.version(&version)?, }; let mut data = self.data.lock(); @@ -268,6 +305,8 @@ impl SessionImpl { } data.consensus_session.consensus_job_mut().transport_mut().version = Some(version.clone()); + data.consensus_session.consensus_job_mut().transport_mut().origin = origin.clone(); + data.origin = origin; data.version = Some(version.clone()); data.is_shadow_decryption = Some(is_shadow_decryption); data.is_broadcast_session = Some(is_broadcast_session); @@ -298,7 +337,7 @@ impl SessionImpl { &DecryptionMessage::PartialDecryption(ref message) => self.on_partial_decryption(sender, message), &DecryptionMessage::DecryptionSessionError(ref message) => - self.process_node_error(Some(&sender), Error::Io(message.error.clone())), + self.process_node_error(Some(&sender), message.error.clone()), &DecryptionMessage::DecryptionSessionCompleted(ref message) => self.on_session_completed(sender, message), &DecryptionMessage::DecryptionSessionDelegation(ref message) => @@ -323,7 +362,7 @@ impl SessionImpl { data.delegation_status = Some(DelegationStatus::DelegatedFrom(sender.clone(), message.session_nonce)); } - self.initialize(message.version.clone().into(), message.is_shadow_decryption, message.is_broadcast_session) + self.initialize(message.origin.clone().map(Into::into), message.version.clone().into(), message.is_shadow_decryption, message.is_broadcast_session) } /// When delegated session is completed on other node. @@ -364,6 +403,7 @@ impl SessionImpl { .unwrap_or(false); data.consensus_session.consensus_job_mut().executor_mut().set_has_key_share(has_key_share); data.version = Some(version); + data.origin = message.origin.clone().map(Into::into); } data.consensus_session.on_consensus_message(&sender, &message.message)?; @@ -392,18 +432,23 @@ impl SessionImpl { }; let mut data = self.data.lock(); - let key_version = key_share.version(data.version.as_ref().ok_or(Error::InvalidMessage)?) - .map_err(|e| Error::KeyStorage(e.into()))?.hash.clone(); + let key_version = key_share.version(data.version.as_ref().ok_or(Error::InvalidMessage)?)?.hash.clone(); let requester_public = data.consensus_session.consensus_job().executor().requester() .ok_or(Error::InvalidStateForRequest)? .public(&self.core.meta.id) - .ok_or(Error::InsufficientRequesterData)?; + .map_err(Error::InsufficientRequesterData)?; let decryption_job = DecryptionJob::new_on_slave(self.core.meta.self_node_id.clone(), self.core.access_key.clone(), requester_public.clone(), key_share.clone(), key_version)?; let decryption_transport = self.core.decryption_transport(false); + // update flags if not on master + if self.core.meta.self_node_id != self.core.meta.master_node_id { + data.is_shadow_decryption = Some(message.is_shadow_decryption); + data.is_broadcast_session = Some(message.is_broadcast_session); + } + // respond to request - data.consensus_session.on_job_request(sender, PartialDecryptionRequest { + let partial_decryption = data.consensus_session.on_job_request(sender, PartialDecryptionRequest { id: message.request_id.clone().into(), is_shadow_decryption: message.is_shadow_decryption, is_broadcast_session: message.is_broadcast_session, @@ -417,7 +462,7 @@ impl SessionImpl { self.core.access_key.clone(), requester_public, key_share.clone(), key_version, message.is_shadow_decryption, message.is_broadcast_session)?; Self::create_broadcast_decryption_job(&self.core, &mut *data, consensus_group, broadcast_decryption_job, - message.request_id.clone().into())?; + message.request_id.clone().into(), Some(partial_decryption.take_response()))?; } Ok(()) @@ -430,38 +475,52 @@ impl SessionImpl { debug_assert!(sender != &self.core.meta.self_node_id); let mut data = self.data.lock(); - if self.core.meta.self_node_id == self.core.meta.master_node_id { + let is_master_node = self.core.meta.self_node_id == self.core.meta.master_node_id; + let result = if is_master_node { data.consensus_session.on_job_response(sender, PartialDecryptionResponse { request_id: message.request_id.clone().into(), shadow_point: message.shadow_point.clone().into(), decrypt_shadow: message.decrypt_shadow.clone(), })?; - } else { - match data.broadcast_job_session.as_mut() { - Some(broadcast_job_session) => broadcast_job_session.on_partial_response(sender, PartialDecryptionResponse { - request_id: message.request_id.clone().into(), - shadow_point: message.shadow_point.clone().into(), - decrypt_shadow: message.decrypt_shadow.clone(), - })?, - None => return Err(Error::TooEarlyForRequest), + + if data.consensus_session.state() != ConsensusSessionState::Finished && + data.consensus_session.state() != ConsensusSessionState::Failed { + return Ok(()); } - } - if data.consensus_session.state() != ConsensusSessionState::Finished { - return Ok(()); - } + // send completion signal to all nodes, except for rejected nodes + if is_master_node { + for node in data.consensus_session.consensus_non_rejected_nodes() { + self.core.cluster.send(&node, Message::Decryption(DecryptionMessage::DecryptionSessionCompleted(DecryptionSessionCompleted { + session: self.core.meta.id.clone().into(), + sub_session: self.core.access_key.clone().into(), + session_nonce: self.core.nonce, + })))?; + } + } - // send compeltion signal to all nodes, except for rejected nodes - for node in data.consensus_session.consensus_non_rejected_nodes() { - self.core.cluster.send(&node, Message::Decryption(DecryptionMessage::DecryptionSessionCompleted(DecryptionSessionCompleted { - session: self.core.meta.id.clone().into(), - sub_session: self.core.access_key.clone().into(), - session_nonce: self.core.nonce, - })))?; - } + data.consensus_session.result() + } else { + match data.broadcast_job_session.as_mut() { + Some(broadcast_job_session) => { + broadcast_job_session.on_partial_response(sender, PartialDecryptionResponse { + request_id: message.request_id.clone().into(), + shadow_point: message.shadow_point.clone().into(), + decrypt_shadow: message.decrypt_shadow.clone(), + })?; + + if broadcast_job_session.state() != JobSessionState::Finished && + broadcast_job_session.state() != JobSessionState::Failed { + return Ok(()); + } - let result = data.consensus_session.result()?; - Self::set_decryption_result(&self.core, &mut *data, Ok(result)); + broadcast_job_session.result() + }, + None => return Err(Error::InvalidMessage), + } + }; + + Self::set_decryption_result(&self.core, &mut *data, result); Ok(()) } @@ -504,7 +563,7 @@ impl SessionImpl { match { match node { - Some(node) => data.consensus_session.on_node_error(node), + Some(node) => data.consensus_session.on_node_error(node, error.clone()), None => data.consensus_session.on_session_timeout(), } } { @@ -541,30 +600,33 @@ impl SessionImpl { Some(key_share) => key_share, }; - let key_version = key_share.version(version).map_err(|e| Error::KeyStorage(e.into()))?.hash.clone(); + let key_version = key_share.version(version)?.hash.clone(); let requester = data.consensus_session.consensus_job().executor().requester().ok_or(Error::InvalidStateForRequest)?.clone(); - let requester_public = requester.public(&core.meta.id).ok_or(Error::InsufficientRequesterData)?; + let requester_public = requester.public(&core.meta.id).map_err(Error::InsufficientRequesterData)?; let consensus_group = data.consensus_session.select_consensus_group()?.clone(); let decryption_job = DecryptionJob::new_on_master(core.meta.self_node_id.clone(), core.access_key.clone(), requester_public.clone(), key_share.clone(), key_version, is_shadow_decryption, is_broadcast_session)?; - let decryption_request_id = decryption_job.request_id().clone().expect("TODO"); + let decryption_request_id = decryption_job.request_id().clone() + .expect("DecryptionJob always have request_id when created on master; it is created using new_on_master above; qed"); let decryption_transport = core.decryption_transport(false); - data.consensus_session.disseminate_jobs(decryption_job, decryption_transport, data.is_broadcast_session.expect("TODO"))?; + let is_broadcast_session = data.is_broadcast_session + .expect("disseminate_jobs is called on master node only; on master node is_broadcast_session is filled during initialization; qed"); + let self_response = data.consensus_session.disseminate_jobs(decryption_job, decryption_transport, is_broadcast_session)?; // ...and prepare decryption job session if we need to broadcast result - if data.is_broadcast_session.expect("TODO") { + if is_broadcast_session { let broadcast_decryption_job = DecryptionJob::new_on_master(core.meta.self_node_id.clone(), core.access_key.clone(), requester_public, key_share.clone(), key_version, is_shadow_decryption, is_broadcast_session)?; Self::create_broadcast_decryption_job(&core, data, consensus_group, broadcast_decryption_job, - decryption_request_id)?; + decryption_request_id, self_response)?; } Ok(()) } /// Create broadcast decryption job. - fn create_broadcast_decryption_job(core: &SessionCore, data: &mut SessionData, mut consensus_group: BTreeSet, mut job: DecryptionJob, request_id: Secret) -> Result<(), Error> { + fn create_broadcast_decryption_job(core: &SessionCore, data: &mut SessionData, mut consensus_group: BTreeSet, mut job: DecryptionJob, request_id: Secret, self_response: Option) -> Result<(), Error> { consensus_group.insert(core.meta.self_node_id.clone()); job.set_request_id(request_id.clone().into()); @@ -574,8 +636,10 @@ impl SessionImpl { master_node_id: core.meta.self_node_id.clone(), self_node_id: core.meta.self_node_id.clone(), threshold: core.meta.threshold, + configured_nodes_count: core.meta.configured_nodes_count, + connected_nodes_count: core.meta.connected_nodes_count, }, job, transport); - job_session.initialize(consensus_group, core.meta.self_node_id != core.meta.master_node_id)?; + job_session.initialize(consensus_group, self_response, core.meta.self_node_id != core.meta.master_node_id)?; data.broadcast_job_session = Some(job_session); Ok(()) @@ -691,6 +755,7 @@ impl JobTransport for DecryptionConsensusTransport { session: self.id.clone().into(), sub_session: self.access_key.clone().into(), session_nonce: self.nonce, + origin: self.origin.clone().map(Into::into), message: ConsensusMessage::InitializeConsensusSession(InitializeConsensusSession { requester: request.into(), version: version.clone().into(), @@ -703,6 +768,7 @@ impl JobTransport for DecryptionConsensusTransport { session: self.id.clone().into(), sub_session: self.access_key.clone().into(), session_nonce: self.nonce, + origin: None, message: ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { is_confirmed: response, }) @@ -746,12 +812,34 @@ impl JobTransport for DecryptionJobTransport { } } +#[cfg(test)] +pub fn create_default_decryption_session() -> Arc { + use acl_storage::DummyAclStorage; + use key_server_cluster::cluster::tests::DummyCluster; + + Arc::new(SessionImpl::new(SessionParams { + meta: SessionMeta { + id: Default::default(), + self_node_id: Default::default(), + master_node_id: Default::default(), + threshold: 0, + configured_nodes_count: 0, + connected_nodes_count: 0, + }, + access_key: Secret::zero(), + key_share: Default::default(), + acl_storage: Arc::new(DummyAclStorage::default()), + cluster: Arc::new(DummyCluster::new(Default::default())), + nonce: 0, + }, Some(Requester::Public(2.into()))).unwrap()) +} + #[cfg(test)] mod tests { use std::sync::Arc; use std::collections::{BTreeMap, VecDeque}; use acl_storage::DummyAclStorage; - use ethkey::{self, KeyPair, Random, Generator, Public, Secret}; + use ethkey::{self, KeyPair, Random, Generator, Public, Secret, public_to_address}; use key_server_cluster::{NodeId, DocumentKeyShare, DocumentKeyShareVersion, SessionId, Requester, Error, EncryptedDocumentKeyShadow, SessionMeta}; use key_server_cluster::cluster::tests::DummyCluster; @@ -816,6 +904,8 @@ mod tests { self_node_id: id_numbers.iter().nth(i).clone().unwrap().0, master_node_id: id_numbers.iter().nth(0).clone().unwrap().0, threshold: encrypted_datas[i].threshold, + configured_nodes_count: 5, + connected_nodes_count: 5, }, access_key: access_key.clone(), key_share: Some(encrypted_datas[i].clone()), @@ -879,6 +969,8 @@ mod tests { self_node_id: self_node_id.clone(), master_node_id: self_node_id.clone(), threshold: 0, + configured_nodes_count: 1, + connected_nodes_count: 1, }, access_key: Random.generate().unwrap().secret().clone(), key_share: Some(DocumentKeyShare { @@ -911,6 +1003,8 @@ mod tests { self_node_id: self_node_id.clone(), master_node_id: self_node_id.clone(), threshold: 0, + configured_nodes_count: 1, + connected_nodes_count: 1, }, access_key: Random.generate().unwrap().secret().clone(), key_share: None, @@ -918,7 +1012,7 @@ mod tests { cluster: Arc::new(DummyCluster::new(self_node_id.clone())), nonce: 0, }, Some(Requester::Signature(ethkey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap()))).unwrap(); - assert_eq!(session.initialize(Default::default(), false, false), Err(Error::InvalidMessage)); + assert_eq!(session.initialize(Default::default(), Default::default(), false, false), Err(Error::InvalidMessage)); } #[test] @@ -933,6 +1027,8 @@ mod tests { self_node_id: self_node_id.clone(), master_node_id: self_node_id.clone(), threshold: 2, + configured_nodes_count: 1, + connected_nodes_count: 1, }, access_key: Random.generate().unwrap().secret().clone(), key_share: Some(DocumentKeyShare { @@ -951,24 +1047,25 @@ mod tests { cluster: Arc::new(DummyCluster::new(self_node_id.clone())), nonce: 0, }, Some(Requester::Signature(ethkey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap()))).unwrap(); - assert_eq!(session.initialize(Default::default(), false, false), Err(Error::ConsensusUnreachable)); + assert_eq!(session.initialize(Default::default(), Default::default(), false, false), Err(Error::ConsensusUnreachable)); } #[test] fn fails_to_initialize_when_already_initialized() { let (_, _, _, sessions) = prepare_decryption_sessions(); - assert_eq!(sessions[0].initialize(Default::default(), false, false).unwrap(), ()); - assert_eq!(sessions[0].initialize(Default::default(), false, false).unwrap_err(), Error::InvalidStateForRequest); + assert_eq!(sessions[0].initialize(Default::default(), Default::default(), false, false).unwrap(), ()); + assert_eq!(sessions[0].initialize(Default::default(), Default::default(), false, false).unwrap_err(), Error::InvalidStateForRequest); } #[test] fn fails_to_accept_initialization_when_already_initialized() { let (_, _, _, sessions) = prepare_decryption_sessions(); - assert_eq!(sessions[0].initialize(Default::default(), false, false).unwrap(), ()); + assert_eq!(sessions[0].initialize(Default::default(), Default::default(), false, false).unwrap(), ()); assert_eq!(sessions[0].on_consensus_message(sessions[1].node(), &message::DecryptionConsensusMessage { session: SessionId::default().into(), sub_session: sessions[0].access_key().clone().into(), session_nonce: 0, + origin: None, message: message::ConsensusMessage::InitializeConsensusSession(message::InitializeConsensusSession { requester: Requester::Signature(ethkey::sign( Random.generate().unwrap().secret(), &SessionId::default()).unwrap()).into(), @@ -984,6 +1081,7 @@ mod tests { session: SessionId::default().into(), sub_session: sessions[0].access_key().clone().into(), session_nonce: 0, + origin: None, message: message::ConsensusMessage::InitializeConsensusSession(message::InitializeConsensusSession { requester: Requester::Signature(ethkey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap()).into(), @@ -1008,6 +1106,7 @@ mod tests { session: SessionId::default().into(), sub_session: sessions[0].access_key().clone().into(), session_nonce: 0, + origin: None, message: message::ConsensusMessage::InitializeConsensusSession(message::InitializeConsensusSession { requester: Requester::Signature(ethkey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap()).into(), @@ -1041,7 +1140,7 @@ mod tests { #[test] fn fails_to_accept_partial_decrypt_twice() { let (_, clusters, _, sessions) = prepare_decryption_sessions(); - sessions[0].initialize(Default::default(), false, false).unwrap(); + sessions[0].initialize(Default::default(), Default::default(), false, false).unwrap(); let mut pd_from = None; let mut pd_msg = None; @@ -1063,17 +1162,17 @@ mod tests { let (_, _, _, sessions) = prepare_decryption_sessions(); assert!(sessions[0].decrypted_secret().is_none()); sessions[0].on_session_timeout(); - assert_eq!(sessions[0].decrypted_secret().unwrap().unwrap_err(), Error::ConsensusUnreachable); + assert_eq!(sessions[0].decrypted_secret().unwrap().unwrap_err(), Error::ConsensusTemporaryUnreachable); } #[test] fn node_is_marked_rejected_when_timed_out_during_initialization_confirmation() { let (_, _, _, sessions) = prepare_decryption_sessions(); - sessions[0].initialize(Default::default(), false, false).unwrap(); + sessions[0].initialize(Default::default(), Default::default(), false, false).unwrap(); // 1 node disconnects => we still can recover secret sessions[0].on_node_timeout(sessions[1].node()); - assert!(sessions[0].data.lock().consensus_session.consensus_job().rejects().contains(sessions[1].node())); + assert!(sessions[0].data.lock().consensus_session.consensus_job().rejects().contains_key(sessions[1].node())); assert!(sessions[0].state() == ConsensusSessionState::EstablishingConsensus); // 2 node are disconnected => we can not recover secret @@ -1086,8 +1185,8 @@ mod tests { let (_, clusters, acl_storages, sessions) = prepare_decryption_sessions(); let key_pair = Random.generate().unwrap(); - acl_storages[1].prohibit(key_pair.public().clone(), SessionId::default()); - sessions[0].initialize(Default::default(), false, false).unwrap(); + acl_storages[1].prohibit(public_to_address(key_pair.public()), SessionId::default()); + sessions[0].initialize(Default::default(), Default::default(), false, false).unwrap(); do_messages_exchange_until(&clusters, &sessions, |_, _, _| sessions[0].state() == ConsensusSessionState::WaitingForPartialResults).unwrap(); @@ -1099,7 +1198,7 @@ mod tests { #[test] fn session_does_not_fail_if_requested_node_disconnects() { let (_, clusters, _, sessions) = prepare_decryption_sessions(); - sessions[0].initialize(Default::default(), false, false).unwrap(); + sessions[0].initialize(Default::default(), Default::default(), false, false).unwrap(); do_messages_exchange_until(&clusters, &sessions, |_, _, _| sessions[0].state() == ConsensusSessionState::WaitingForPartialResults).unwrap(); @@ -1115,7 +1214,7 @@ mod tests { #[test] fn session_does_not_fail_if_node_with_shadow_point_disconnects() { let (_, clusters, _, sessions) = prepare_decryption_sessions(); - sessions[0].initialize(Default::default(), false, false).unwrap(); + sessions[0].initialize(Default::default(), Default::default(), false, false).unwrap(); do_messages_exchange_until(&clusters, &sessions, |_, _, _| sessions[0].state() == ConsensusSessionState::WaitingForPartialResults && sessions[0].data.lock().consensus_session.computation_job().responses().len() == 2).unwrap(); @@ -1132,7 +1231,7 @@ mod tests { #[test] fn session_restarts_if_confirmed_node_disconnects() { let (_, clusters, _, sessions) = prepare_decryption_sessions(); - sessions[0].initialize(Default::default(), false, false).unwrap(); + sessions[0].initialize(Default::default(), Default::default(), false, false).unwrap(); do_messages_exchange_until(&clusters, &sessions, |_, _, _| sessions[0].state() == ConsensusSessionState::WaitingForPartialResults).unwrap(); @@ -1140,14 +1239,14 @@ mod tests { let disconnected = sessions[0].data.lock().consensus_session.computation_job().requests().iter().cloned().nth(0).unwrap(); sessions[0].on_node_timeout(&disconnected); assert_eq!(sessions[0].state(), ConsensusSessionState::EstablishingConsensus); - assert!(sessions[0].data.lock().consensus_session.computation_job().rejects().contains(&disconnected)); + assert!(sessions[0].data.lock().consensus_session.computation_job().rejects().contains_key(&disconnected)); assert!(!sessions[0].data.lock().consensus_session.computation_job().requests().contains(&disconnected)); } #[test] fn session_does_not_fail_if_non_master_node_disconnects_from_non_master_node() { let (_, clusters, _, sessions) = prepare_decryption_sessions(); - sessions[0].initialize(Default::default(), false, false).unwrap(); + sessions[0].initialize(Default::default(), Default::default(), false, false).unwrap(); do_messages_exchange_until(&clusters, &sessions, |_, _, _| sessions[0].state() == ConsensusSessionState::WaitingForPartialResults).unwrap(); @@ -1162,7 +1261,7 @@ mod tests { let (_, clusters, _, sessions) = prepare_decryption_sessions(); // now let's try to do a decryption - sessions[0].initialize(Default::default(), false, false).unwrap(); + sessions[0].initialize(Default::default(), Default::default(), false, false).unwrap(); do_messages_exchange(&clusters, &sessions).unwrap(); @@ -1184,7 +1283,7 @@ mod tests { let (key_pair, clusters, _, sessions) = prepare_decryption_sessions(); // now let's try to do a decryption - sessions[0].initialize(Default::default(), true, false).unwrap(); + sessions[0].initialize(Default::default(), Default::default(), true, false).unwrap(); do_messages_exchange(&clusters, &sessions).unwrap(); @@ -1201,10 +1300,10 @@ mod tests { assert!(decrypted_secret.common_point.is_some()); assert!(decrypted_secret.decrypt_shadows.is_some()); // check that KS client is able to restore original secret - use ethcrypto::DEFAULT_MAC; - use ethcrypto::ecies::decrypt; + use crypto::DEFAULT_MAC; + use ethkey::crypto::ecies::decrypt; let decrypt_shadows: Vec<_> = decrypted_secret.decrypt_shadows.unwrap().into_iter() - .map(|c| Secret::from_slice(&decrypt(key_pair.secret(), &DEFAULT_MAC, &c).unwrap())) + .map(|c| Secret::from_slice(&decrypt(key_pair.secret(), &DEFAULT_MAC, &c).unwrap()).unwrap()) .collect(); let decrypted_secret = math::decrypt_with_shadow_coefficients(decrypted_secret.decrypted_secret, decrypted_secret.common_point.unwrap(), decrypt_shadows).unwrap(); assert_eq!(decrypted_secret, SECRET_PLAIN.into()); @@ -1215,12 +1314,12 @@ mod tests { let (key_pair, clusters, acl_storages, sessions) = prepare_decryption_sessions(); // now let's try to do a decryption - sessions[0].initialize(Default::default(), false, false).unwrap(); + sessions[0].initialize(Default::default(), Default::default(), false, false).unwrap(); // we need 4 out of 5 nodes to agree to do a decryption // let's say that 2 of these nodes are disagree - acl_storages[1].prohibit(key_pair.public().clone(), SessionId::default()); - acl_storages[2].prohibit(key_pair.public().clone(), SessionId::default()); + acl_storages[1].prohibit(public_to_address(key_pair.public()), SessionId::default()); + acl_storages[2].prohibit(public_to_address(key_pair.public()), SessionId::default()); assert_eq!(do_messages_exchange(&clusters, &sessions).unwrap_err(), Error::ConsensusUnreachable); @@ -1235,10 +1334,10 @@ mod tests { // we need 4 out of 5 nodes to agree to do a decryption // let's say that 1 of these nodes (master) is disagree - acl_storages[0].prohibit(key_pair.public().clone(), SessionId::default()); + acl_storages[0].prohibit(public_to_address(key_pair.public()), SessionId::default()); // now let's try to do a decryption - sessions[0].initialize(Default::default(), false, false).unwrap(); + sessions[0].initialize(Default::default(), Default::default(), false, false).unwrap(); do_messages_exchange(&clusters, &sessions).unwrap(); @@ -1278,7 +1377,7 @@ mod tests { ); // now let's try to do a decryption - sessions[1].delegate(sessions[0].core.meta.self_node_id.clone(), Default::default(), false, false).unwrap(); + sessions[1].delegate(sessions[0].core.meta.self_node_id.clone(), Default::default(), Default::default(), false, false).unwrap(); do_messages_exchange(&clusters, &sessions).unwrap(); // now check that: @@ -1304,7 +1403,7 @@ mod tests { } // now let's try to do a decryption - sessions[0].initialize(Default::default(), false, false).unwrap(); + sessions[0].initialize(Default::default(), Default::default(), false, false).unwrap(); do_messages_exchange(&clusters, &sessions).unwrap(); assert_eq!(sessions[0].decrypted_secret().unwrap().unwrap(), EncryptedDocumentKeyShadow { @@ -1317,13 +1416,52 @@ mod tests { #[test] fn decryption_result_restored_on_all_nodes_if_broadcast_session_is_completed() { let (_, clusters, _, sessions) = prepare_decryption_sessions(); - sessions[0].initialize(Default::default(), false, true).unwrap(); + sessions[0].initialize(Default::default(), Default::default(), false, true).unwrap(); do_messages_exchange(&clusters, &sessions).unwrap(); // decryption result must be the same and available on 4 nodes let result = sessions[0].decrypted_secret(); assert!(result.clone().unwrap().is_ok()); + assert_eq!(result.clone().unwrap().unwrap(), EncryptedDocumentKeyShadow { + decrypted_secret: SECRET_PLAIN.into(), + common_point: None, + decrypt_shadows: None, + }); assert_eq!(3, sessions.iter().skip(1).filter(|s| s.decrypted_secret() == result).count()); assert_eq!(1, sessions.iter().skip(1).filter(|s| s.decrypted_secret().is_none()).count()); } + + #[test] + fn decryption_shadows_restored_on_all_nodes_if_shadow_broadcast_session_is_completed() { + let (key_pair, clusters, _, sessions) = prepare_decryption_sessions(); + sessions[0].initialize(Default::default(), Default::default(), true, true).unwrap(); + do_messages_exchange(&clusters, &sessions).unwrap(); + + // decryption shadows must be the same and available on 4 nodes + let broadcast_shadows = sessions[0].broadcast_shadows(); + assert!(broadcast_shadows.is_some()); + assert_eq!(3, sessions.iter().skip(1).filter(|s| s.broadcast_shadows() == broadcast_shadows).count()); + assert_eq!(1, sessions.iter().skip(1).filter(|s| s.broadcast_shadows().is_none()).count()); + + // 4 nodes must be able to recover original secret + use crypto::DEFAULT_MAC; + use ethkey::crypto::ecies::decrypt; + let result = sessions[0].decrypted_secret().unwrap().unwrap(); + assert_eq!(3, sessions.iter().skip(1).filter(|s| s.decrypted_secret() == Some(Ok(result.clone()))).count()); + let decrypt_shadows: Vec<_> = result.decrypt_shadows.unwrap().into_iter() + .map(|c| Secret::from_slice(&decrypt(key_pair.secret(), &DEFAULT_MAC, &c).unwrap()).unwrap()) + .collect(); + let decrypted_secret = math::decrypt_with_shadow_coefficients(result.decrypted_secret, result.common_point.unwrap(), decrypt_shadows).unwrap(); + assert_eq!(decrypted_secret, SECRET_PLAIN.into()); + } + + #[test] + fn decryption_session_origin_is_known_to_all_initialized_nodes() { + let (_, clusters, _, sessions) = prepare_decryption_sessions(); + sessions[0].initialize(Some(1.into()), Default::default(), true, true).unwrap(); + do_messages_exchange(&clusters, &sessions).unwrap(); + + // all session must have origin set + assert_eq!(5, sessions.iter().filter(|s| s.origin() == Some(1.into())).count()); + } } diff --git a/secret_store/src/key_server_cluster/client_sessions/encryption_session.rs b/secret_store/src/key_server_cluster/client_sessions/encryption_session.rs index eafac6fd25693350ce921911bd941720bb3949ef..70532b69052171aa061b4ba0f672e7390801fdb4 100644 --- a/secret_store/src/key_server_cluster/client_sessions/encryption_session.rs +++ b/secret_store/src/key_server_cluster/client_sessions/encryption_session.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -19,8 +19,10 @@ use std::fmt::{Debug, Formatter, Error as FmtError}; use std::time; use std::sync::Arc; use parking_lot::{Condvar, Mutex}; +use ethereum_types::Address; use ethkey::Public; -use key_server_cluster::{Error, NodeId, SessionId, Requester, KeyStorage, DocumentKeyShare}; +use key_server_cluster::{Error, NodeId, SessionId, Requester, KeyStorage, + DocumentKeyShare, ServerKeyId}; use key_server_cluster::cluster::Cluster; use key_server_cluster::cluster_sessions::ClusterSession; use key_server_cluster::message::{Message, EncryptionMessage, InitializeEncryptionSession, @@ -107,7 +109,7 @@ pub enum SessionState { impl SessionImpl { /// Create new encryption session. pub fn new(params: SessionParams) -> Result { - check_encrypted_data(¶ms.encrypted_data)?; + check_encrypted_data(params.encrypted_data.as_ref())?; Ok(SessionImpl { id: params.id, @@ -133,9 +135,9 @@ impl SessionImpl { /// Wait for session completion. pub fn wait(&self, timeout: Option) -> Result<(), Error> { Self::wait_session(&self.completed, &self.data, timeout, |data| data.result.clone()) + .expect("wait_session returns Some if called without timeout; qed") } - /// Start new session initialization. This must be called on master node. pub fn initialize(&self, requester: Requester, common_point: Public, encrypted_point: Public) -> Result<(), Error> { let mut data = self.data.lock(); @@ -155,17 +157,10 @@ impl SessionImpl { // TODO [Reliability]: there could be situation when some nodes have failed to store encrypted data // => potential problems during restore. some confirmation step is needed (2pc)? // save encryption data - if let Some(mut encrypted_data) = self.encrypted_data.clone() { - // check that the requester is the author of the encrypted data - let requester_address = requester.address(&self.id).ok_or(Error::InsufficientRequesterData)?; - if encrypted_data.author != requester_address { - return Err(Error::AccessDenied); - } - - encrypted_data.common_point = Some(common_point.clone()); - encrypted_data.encrypted_point = Some(encrypted_point.clone()); - self.key_storage.update(self.id.clone(), encrypted_data) - .map_err(|e| Error::KeyStorage(e.into()))?; + if let Some(encrypted_data) = self.encrypted_data.clone() { + let requester_address = requester.address(&self.id).map_err(Error::InsufficientRequesterData)?; + update_encrypted_data(&self.key_storage, self.id.clone(), + encrypted_data, requester_address, common_point.clone(), encrypted_point.clone())?; } // start initialization @@ -199,18 +194,11 @@ impl SessionImpl { } // check that the requester is the author of the encrypted data - if let Some(mut encrypted_data) = self.encrypted_data.clone() { + if let Some(encrypted_data) = self.encrypted_data.clone() { let requester: Requester = message.requester.clone().into(); - let requestor_address = requester.address(&self.id).ok_or(Error::InsufficientRequesterData)?; - if encrypted_data.author != requestor_address { - return Err(Error::AccessDenied); - } - - // save encryption data - encrypted_data.common_point = Some(message.common_point.clone().into()); - encrypted_data.encrypted_point = Some(message.encrypted_point.clone().into()); - self.key_storage.update(self.id.clone(), encrypted_data) - .map_err(|e| Error::KeyStorage(e.into()))?; + let requester_address = requester.address(&self.id).map_err(Error::InsufficientRequesterData)?; + update_encrypted_data(&self.key_storage, self.id.clone(), + encrypted_data, requester_address, message.common_point.clone().into(), message.encrypted_point.clone().into())?; } // update state @@ -318,7 +306,7 @@ impl ClusterSession for SessionImpl { &EncryptionMessage::ConfirmEncryptionInitialization(ref message) => self.on_confirm_initialization(sender.clone(), message), &EncryptionMessage::EncryptionSessionError(ref message) => { - self.on_session_error(sender, Error::Io(message.error.clone().into())); + self.on_session_error(sender, message.error.clone()); Ok(()) }, }, @@ -333,13 +321,27 @@ impl Debug for SessionImpl { } } -fn check_encrypted_data(encrypted_data: &Option) -> Result<(), Error> { - if let &Some(ref encrypted_data) = encrypted_data { +/// Check that common_point and encrypted point are not yet set in key share. +pub fn check_encrypted_data(key_share: Option<&DocumentKeyShare>) -> Result<(), Error> { + if let Some(key_share) = key_share { // check that common_point and encrypted_point are still not set yet - if encrypted_data.common_point.is_some() || encrypted_data.encrypted_point.is_some() { - return Err(Error::CompletedSessionId); + if key_share.common_point.is_some() || key_share.encrypted_point.is_some() { + return Err(Error::DocumentKeyAlreadyStored); } } Ok(()) } + +/// Update key share with encrypted document key. +pub fn update_encrypted_data(key_storage: &Arc, key_id: ServerKeyId, mut key_share: DocumentKeyShare, author: Address, common_point: Public, encrypted_point: Public) -> Result<(), Error> { + // author must be the same + if key_share.author != author { + return Err(Error::AccessDenied); + } + + // save encryption data + key_share.common_point = Some(common_point); + key_share.encrypted_point = Some(encrypted_point); + key_storage.update(key_id, key_share) +} diff --git a/secret_store/src/key_server_cluster/client_sessions/generation_session.rs b/secret_store/src/key_server_cluster/client_sessions/generation_session.rs index 1f1df70ce3b0b63ef9be3a9458cae60d686766aa..7001ccf69ec49a721d01d3a3cc4b3de3e52fa2ab 100644 --- a/secret_store/src/key_server_cluster/client_sessions/generation_session.rs +++ b/secret_store/src/key_server_cluster/client_sessions/generation_session.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -16,7 +16,7 @@ use std::collections::{BTreeSet, BTreeMap, VecDeque}; use std::fmt::{Debug, Formatter, Error as FmtError}; -use std::time; +use std::time::Duration; use std::sync::Arc; use parking_lot::{Condvar, Mutex}; use ethereum_types::Address; @@ -82,6 +82,8 @@ struct SessionData { author: Option
, // === Values, filled when session initialization is completed === + /// Session origin (if any). + origin: Option
, /// Is zero secret generation session? is_zero: Option, /// Threshold value for this DKG. Only `threshold + 1` will be able to collectively recreate joint secret, @@ -217,6 +219,7 @@ impl SessionImpl { simulate_faulty_behaviour: false, master: None, author: None, + origin: None, is_zero: None, threshold: None, derived_point: None, @@ -251,8 +254,13 @@ impl SessionImpl { self.data.lock().state.clone() } + /// Get session origin. + pub fn origin(&self) -> Option
{ + self.data.lock().origin.clone() + } + /// Wait for session completion. - pub fn wait(&self, timeout: Option) -> Result { + pub fn wait(&self, timeout: Option) -> Option> { Self::wait_session(&self.completed, &self.data, timeout, |data| data.joint_public_and_secret.clone() .map(|r| r.map(|r| r.0.clone()))) } @@ -263,7 +271,7 @@ impl SessionImpl { } /// Start new session initialization. This must be called on master node. - pub fn initialize(&self, author: Address, is_zero: bool, threshold: usize, nodes: InitializationNodes) -> Result<(), Error> { + pub fn initialize(&self, origin: Option
, author: Address, is_zero: bool, threshold: usize, nodes: InitializationNodes) -> Result<(), Error> { check_cluster_nodes(self.node(), &nodes.set())?; check_threshold(threshold, &nodes.set())?; @@ -277,6 +285,7 @@ impl SessionImpl { // update state data.master = Some(self.node().clone()); data.author = Some(author.clone()); + data.origin = origin.clone(); data.is_zero = Some(is_zero); data.threshold = Some(threshold); match nodes { @@ -304,6 +313,7 @@ impl SessionImpl { self.cluster.send(&next_node, Message::Generation(GenerationMessage::InitializeSession(InitializeSession { session: self.id.clone().into(), session_nonce: self.nonce, + origin: origin.map(Into::into), author: author.into(), nodes: data.nodes.iter().map(|(k, v)| (k.clone().into(), v.id_number.clone().into())).collect(), is_zero: data.is_zero.expect("is_zero is filled in initialization phase; KD phase follows initialization phase; qed"), @@ -316,7 +326,12 @@ impl SessionImpl { self.complete_initialization(derived_point)?; self.disseminate_keys()?; self.verify_keys()?; - self.complete_generation() + self.complete_generation()?; + + self.data.lock().state = SessionState::Finished; + self.completed.notify_all(); + + Ok(()) } } } @@ -339,7 +354,7 @@ impl SessionImpl { &GenerationMessage::PublicKeyShare(ref message) => self.on_public_key_share(sender.clone(), message), &GenerationMessage::SessionError(ref message) => { - self.on_session_error(sender, Error::Io(message.error.clone().into())); + self.on_session_error(sender, message.error.clone()); Ok(()) }, &GenerationMessage::SessionCompleted(ref message) => @@ -380,6 +395,7 @@ impl SessionImpl { data.author = Some(message.author.clone().into()); data.state = SessionState::WaitingForInitializationComplete; data.nodes = message.nodes.iter().map(|(id, number)| (id.clone().into(), NodeData::with_id_number(number.clone().into()))).collect(); + data.origin = message.origin.clone().map(Into::into); data.is_zero = Some(message.is_zero); data.threshold = Some(message.threshold); @@ -411,6 +427,7 @@ impl SessionImpl { return self.cluster.send(&next_receiver, Message::Generation(GenerationMessage::InitializeSession(InitializeSession { session: self.id.clone().into(), session_nonce: self.nonce, + origin: data.origin.clone().map(Into::into), author: data.author.as_ref().expect("author is filled on initialization step; confrm initialization follows initialization; qed").clone().into(), nodes: data.nodes.iter().map(|(k, v)| (k.clone().into(), v.id_number.clone().into())).collect(), is_zero: data.is_zero.expect("is_zero is filled in initialization phase; KD phase follows initialization phase; qed"), @@ -457,7 +474,7 @@ impl SessionImpl { // simulate failure, if required if data.simulate_faulty_behaviour { - return Err(Error::Io("simulated error".into())); + return Err(Error::Internal("simulated error".into())); } // check state @@ -575,8 +592,7 @@ impl SessionImpl { }; if let Some(ref key_storage) = self.key_storage { - key_storage.insert(self.id.clone(), encrypted_data.clone()) - .map_err(|e| Error::KeyStorage(e.into()))?; + key_storage.insert(self.id.clone(), encrypted_data.clone())?; } // then respond with confirmation @@ -773,8 +789,7 @@ impl SessionImpl { // then save encrypted data to the key storage if let Some(ref key_storage) = self.key_storage { - key_storage.insert(self.id.clone(), encrypted_data.clone()) - .map_err(|e| Error::KeyStorage(e.into()))?; + key_storage.insert(self.id.clone(), encrypted_data.clone())?; } // then distribute encrypted data to every other node @@ -908,23 +923,15 @@ impl Debug for SessionImpl { } } -pub fn check_cluster_nodes(self_node_id: &NodeId, nodes: &BTreeSet) -> Result<(), Error> { - // at least two nodes must be in cluster - if nodes.len() < 1 { - return Err(Error::InvalidNodesCount); - } - // this node must be a part of cluster - if !nodes.contains(self_node_id) { - return Err(Error::InvalidNodesConfiguration); - } - +fn check_cluster_nodes(self_node_id: &NodeId, nodes: &BTreeSet) -> Result<(), Error> { + assert!(nodes.contains(self_node_id)); Ok(()) } -pub fn check_threshold(threshold: usize, nodes: &BTreeSet) -> Result<(), Error> { +fn check_threshold(threshold: usize, nodes: &BTreeSet) -> Result<(), Error> { // at least threshold + 1 nodes are required to collectively decrypt message if threshold >= nodes.len() { - return Err(Error::InvalidThreshold); + return Err(Error::NotEnoughNodesForThreshold); } Ok(()) @@ -932,12 +939,12 @@ pub fn check_threshold(threshold: usize, nodes: &BTreeSet) -> Result<(), #[cfg(test)] pub mod tests { - use std::time; use std::sync::Arc; use std::collections::{BTreeSet, BTreeMap, VecDeque}; + use std::time::Duration; use tokio_core::reactor::Core; use ethereum_types::Address; - use ethkey::{Random, Generator, Public, KeyPair}; + use ethkey::{Random, Generator, KeyPair}; use key_server_cluster::{NodeId, SessionId, Error, KeyStorage, DummyKeyStorage}; use key_server_cluster::message::{self, Message, GenerationMessage}; use key_server_cluster::cluster::tests::{DummyCluster, make_clusters, run_clusters, loop_until, all_connections_established}; @@ -1065,7 +1072,7 @@ pub mod tests { fn make_simple_cluster(threshold: usize, num_nodes: usize) -> Result<(SessionId, NodeId, NodeId, MessageLoop), Error> { let l = MessageLoop::new(num_nodes); - l.master().initialize(Default::default(), false, threshold, l.nodes.keys().cloned().collect::>().into())?; + l.master().initialize(Default::default(), Default::default(), false, threshold, l.nodes.keys().cloned().collect::>().into())?; let session_id = l.session_id.clone(); let master_id = l.master().node().clone(); @@ -1076,28 +1083,13 @@ pub mod tests { #[test] fn initializes_in_cluster_of_single_node() { let l = MessageLoop::new(1); - assert!(l.master().initialize(Default::default(), false, 0, l.nodes.keys().cloned().collect::>().into()).is_ok()); - } - - #[test] - fn fails_to_initialize_if_not_a_part_of_cluster() { - let node_id = math::generate_random_point().unwrap(); - let cluster = Arc::new(DummyCluster::new(node_id.clone())); - let session = SessionImpl::new(SessionParams { - id: SessionId::default(), - self_node_id: node_id.clone(), - key_storage: Some(Arc::new(DummyKeyStorage::default())), - cluster: cluster, - nonce: Some(0), - }); - let cluster_nodes: BTreeSet<_> = (0..2).map(|_| math::generate_random_point().unwrap()).collect(); - assert_eq!(session.initialize(Default::default(), false, 0, cluster_nodes.into()).unwrap_err(), Error::InvalidNodesConfiguration); + assert!(l.master().initialize(Default::default(), Default::default(), false, 0, l.nodes.keys().cloned().collect::>().into()).is_ok()); } #[test] fn fails_to_initialize_if_threshold_is_wrong() { match make_simple_cluster(2, 2) { - Err(Error::InvalidThreshold) => (), + Err(Error::NotEnoughNodesForThreshold) => (), _ => panic!("unexpected"), } } @@ -1105,7 +1097,7 @@ pub mod tests { #[test] fn fails_to_initialize_when_already_initialized() { let (_, _, _, l) = make_simple_cluster(0, 2).unwrap(); - assert_eq!(l.master().initialize(Default::default(), false, 0, l.nodes.keys().cloned().collect::>().into()).unwrap_err(), + assert_eq!(l.master().initialize(Default::default(), Default::default(), false, 0, l.nodes.keys().cloned().collect::>().into()).unwrap_err(), Error::InvalidStateForRequest); } @@ -1176,23 +1168,6 @@ pub mod tests { assert!(l.master().derived_point().unwrap() != passed_point.into()); } - #[test] - fn fails_to_complete_initialization_if_not_a_part_of_cluster() { - let (sid, m, _, l) = make_simple_cluster(0, 2).unwrap(); - let mut nodes = BTreeMap::new(); - nodes.insert(m, math::generate_random_scalar().unwrap()); - nodes.insert(math::generate_random_point().unwrap(), math::generate_random_scalar().unwrap()); - assert_eq!(l.first_slave().on_initialize_session(m, &message::InitializeSession { - session: sid.into(), - session_nonce: 0, - author: Address::default().into(), - nodes: nodes.into_iter().map(|(k, v)| (k.into(), v.into())).collect(), - is_zero: false, - threshold: 0, - derived_point: math::generate_random_point().unwrap().into(), - }).unwrap_err(), Error::InvalidNodesConfiguration); - } - #[test] fn fails_to_complete_initialization_if_threshold_is_wrong() { let (sid, m, s, l) = make_simple_cluster(0, 2).unwrap(); @@ -1202,12 +1177,13 @@ pub mod tests { assert_eq!(l.first_slave().on_initialize_session(m, &message::InitializeSession { session: sid.into(), session_nonce: 0, + origin: None, author: Address::default().into(), nodes: nodes.into_iter().map(|(k, v)| (k.into(), v.into())).collect(), is_zero: false, threshold: 2, derived_point: math::generate_random_point().unwrap().into(), - }).unwrap_err(), Error::InvalidThreshold); + }).unwrap_err(), Error::NotEnoughNodesForThreshold); } #[test] @@ -1323,7 +1299,6 @@ pub mod tests { }).unwrap_err(), Error::InvalidMessage); } - #[test] fn encryption_fails_on_session_timeout() { let (_, _, _, l) = make_simple_cluster(0, 2).unwrap(); @@ -1345,7 +1320,7 @@ pub mod tests { let test_cases = [(0, 5), (2, 5), (3, 5)]; for &(threshold, num_nodes) in &test_cases { let mut l = MessageLoop::new(num_nodes); - l.master().initialize(Default::default(), false, threshold, l.nodes.keys().cloned().collect::>().into()).unwrap(); + l.master().initialize(Default::default(), Default::default(), false, threshold, l.nodes.keys().cloned().collect::>().into()).unwrap(); assert_eq!(l.nodes.len(), num_nodes); // let nodes do initialization + keys dissemination @@ -1377,6 +1352,9 @@ pub mod tests { #[test] fn encryption_session_works_over_network() { + const CONN_TIMEOUT: Duration = Duration::from_millis(300); + const SESSION_TIMEOUT: Duration = Duration::from_millis(1000); + let test_cases = [(1, 3)]; for &(threshold, num_nodes) in &test_cases { let mut core = Core::new().unwrap(); @@ -1386,12 +1364,12 @@ pub mod tests { run_clusters(&clusters); // establish connections - loop_until(&mut core, time::Duration::from_millis(300), || clusters.iter().all(all_connections_established)); + loop_until(&mut core, CONN_TIMEOUT, || clusters.iter().all(all_connections_established)); // run session to completion let session_id = SessionId::default(); - let session = clusters[0].client().new_generation_session(session_id, Public::default(), threshold).unwrap(); - loop_until(&mut core, time::Duration::from_millis(1000), || session.joint_public_and_secret().is_some()); + let session = clusters[0].client().new_generation_session(session_id, Default::default(), Default::default(), threshold).unwrap(); + loop_until(&mut core, SESSION_TIMEOUT, || session.joint_public_and_secret().is_some()); } } diff --git a/secret_store/src/key_server_cluster/client_sessions/mod.rs b/secret_store/src/key_server_cluster/client_sessions/mod.rs index ba2fbd5350084773a6db13832805c4a3eda74d54..133edcffbb233a0a9027a765d7404f8bf012a1cc 100644 --- a/secret_store/src/key_server_cluster/client_sessions/mod.rs +++ b/secret_store/src/key_server_cluster/client_sessions/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/secret_store/src/key_server_cluster/client_sessions/signing_session_ecdsa.rs b/secret_store/src/key_server_cluster/client_sessions/signing_session_ecdsa.rs index ddd4944129f81349a8baf9db574f9d603b5518b9..670fa138f2cd5b44f37c03557511958ec4a0d5e2 100644 --- a/secret_store/src/key_server_cluster/client_sessions/signing_session_ecdsa.rs +++ b/secret_store/src/key_server_cluster/client_sessions/signing_session_ecdsa.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -187,6 +187,8 @@ impl SessionImpl { master_node_id: params.meta.master_node_id, self_node_id: params.meta.self_node_id, threshold: params.meta.threshold * 2, + configured_nodes_count: params.meta.configured_nodes_count, + connected_nodes_count: params.meta.connected_nodes_count, }, consensus_executor: match requester { Some(requester) => KeyAccessJob::new_on_master(params.meta.id.clone(), params.acl_storage.clone(), requester), @@ -222,6 +224,7 @@ impl SessionImpl { /// Wait for session completion. pub fn wait(&self) -> Result { Self::wait_session(&self.core.completed, &self.data, None, |data| data.result.clone()) + .expect("wait_session returns Some if called without timeout; qed") } /// Delegate session to other node. @@ -258,7 +261,7 @@ impl SessionImpl { // check if version exists let key_version = match self.core.key_share.as_ref() { None => return Err(Error::InvalidMessage), - Some(key_share) => key_share.version(&version).map_err(|e| Error::KeyStorage(e.into()))?, + Some(key_share) => key_share.version(&version)?, }; // select nodes to participate in consensus etablish session @@ -310,7 +313,7 @@ impl SessionImpl { &EcdsaSigningMessage::EcdsaPartialSignature(ref message) => self.on_partial_signature(sender, message), &EcdsaSigningMessage::EcdsaSigningSessionError(ref message) => - self.process_node_error(Some(&sender), Error::Io(message.error.clone())), + self.process_node_error(Some(&sender), message.error.clone()), &EcdsaSigningMessage::EcdsaSigningSessionCompleted(ref message) => self.on_session_completed(sender, message), &EcdsaSigningMessage::EcdsaSigningSessionDelegation(ref message) => @@ -385,8 +388,7 @@ impl SessionImpl { let key_share = self.core.key_share.as_ref() .expect("this is master node; master node is selected so that it has key version; qed"); let key_version = key_share.version(data.version.as_ref() - .expect("this is master node; master node is selected so that it has key version; qed") - ).map_err(|e| Error::KeyStorage(e.into()))?; + .expect("this is master node; master node is selected so that it has key version; qed"))?; let consensus_group = data.consensus_session.select_consensus_group()?.clone(); let mut other_consensus_group_nodes = consensus_group.clone(); @@ -402,7 +404,7 @@ impl SessionImpl { session_nonce: n, message: m, })); - sig_nonce_generation_session.initialize(Default::default(), false, key_share.threshold, consensus_group_map.clone().into())?; + sig_nonce_generation_session.initialize(Default::default(), Default::default(), false, key_share.threshold, consensus_group_map.clone().into())?; data.sig_nonce_generation_session = Some(sig_nonce_generation_session); // start generation of inversed nonce computation session @@ -414,7 +416,7 @@ impl SessionImpl { session_nonce: n, message: m, })); - inv_nonce_generation_session.initialize(Default::default(), false, key_share.threshold, consensus_group_map.clone().into())?; + inv_nonce_generation_session.initialize(Default::default(), Default::default(), false, key_share.threshold, consensus_group_map.clone().into())?; data.inv_nonce_generation_session = Some(inv_nonce_generation_session); // start generation of zero-secret shares for inversed nonce computation session @@ -426,7 +428,7 @@ impl SessionImpl { session_nonce: n, message: m, })); - inv_zero_generation_session.initialize(Default::default(), true, key_share.threshold * 2, consensus_group_map.clone().into())?; + inv_zero_generation_session.initialize(Default::default(), Default::default(), true, key_share.threshold * 2, consensus_group_map.clone().into())?; data.inv_zero_generation_session = Some(inv_zero_generation_session); data.state = SessionState::NoncesGenerating; @@ -642,7 +644,6 @@ impl SessionImpl { Self::compute_inversed_nonce_coeff(&self.core, &*data)? }; - let version = data.version.as_ref().ok_or(Error::InvalidMessage)?.clone(); let message_hash = data.message_hash .expect("we are on master node; on master node message_hash is filled in initialize(); on_generation_message follows initialize; qed"); @@ -679,7 +680,7 @@ impl SessionImpl { let inv_nonce_share = data.inv_nonce_generation_session.as_ref().expect(nonce_exists_proof).joint_public_and_secret().expect(nonce_exists_proof)?.2; let version = data.version.as_ref().ok_or(Error::InvalidMessage)?.clone(); - let key_version = key_share.version(&version).map_err(|e| Error::KeyStorage(e.into()))?.hash.clone(); + let key_version = key_share.version(&version)?.hash.clone(); let signing_job = EcdsaSigningJob::new_on_slave(key_share.clone(), key_version, sig_nonce_public, inv_nonce_share)?; let signing_transport = self.core.signing_transport(); @@ -688,7 +689,7 @@ impl SessionImpl { id: message.request_id.clone().into(), inversed_nonce_coeff: message.inversed_nonce_coeff.clone().into(), message_hash: message.message_hash.clone().into(), - }, signing_job, signing_transport) + }, signing_job, signing_transport).map(|_| ()) } /// When partial signature is received. @@ -743,7 +744,7 @@ impl SessionImpl { match { match node { - Some(node) => data.consensus_session.on_node_error(node), + Some(node) => data.consensus_session.on_node_error(node, error.clone()), None => data.consensus_session.on_session_timeout(), } } { @@ -969,6 +970,14 @@ impl Cluster for NonceGenerationTransport where F: Fn(SessionId, Secret, u fn nodes(&self) -> BTreeSet { self.cluster.nodes() } + + fn configured_nodes_count(&self) -> usize { + self.cluster.configured_nodes_count() + } + + fn connected_nodes_count(&self) -> usize { + self.cluster.connected_nodes_count() + } } impl SessionCore { @@ -987,9 +996,9 @@ impl SessionCore { Some(key_share) => key_share, }; - let key_version = key_share.version(version).map_err(|e| Error::KeyStorage(e.into()))?.hash.clone(); + let key_version = key_share.version(version)?.hash.clone(); let signing_job = EcdsaSigningJob::new_on_master(key_share.clone(), key_version, nonce_public, inv_nonce_share, inversed_nonce_coeff, message_hash)?; - consensus_session.disseminate_jobs(signing_job, self.signing_transport(), false) + consensus_session.disseminate_jobs(signing_job, self.signing_transport(), false).map(|_| ()) } } @@ -1054,7 +1063,7 @@ mod tests { use std::sync::Arc; use std::collections::{BTreeSet, BTreeMap, VecDeque}; use ethereum_types::H256; - use ethkey::{self, Random, Generator, KeyPair, verify_public}; + use ethkey::{self, Random, Generator, KeyPair, verify_public, public_to_address}; use acl_storage::DummyAclStorage; use key_server_cluster::{NodeId, DummyKeyStorage, SessionId, SessionMeta, Error, KeyStorage}; use key_server_cluster::cluster_sessions::ClusterSession; @@ -1098,6 +1107,8 @@ mod tests { self_node_id: gl_node_id.clone(), master_node_id: master_node_id.clone(), threshold: gl_node.key_storage.get(&session_id).unwrap().unwrap().threshold, + configured_nodes_count: gl.nodes.len(), + connected_nodes_count: gl.nodes.len(), }, access_key: "834cb736f02d9c968dfaf0c37658a1d86ff140554fc8b59c9fdad5a8cf810eec".parse().unwrap(), key_share: Some(gl_node.key_storage.get(&session_id).unwrap().unwrap()), @@ -1165,7 +1176,7 @@ mod tests { fn prepare_signing_sessions(threshold: usize, num_nodes: usize) -> (KeyGenerationMessageLoop, MessageLoop) { // run key generation sessions let mut gl = KeyGenerationMessageLoop::new(num_nodes); - gl.master().initialize(Default::default(), false, threshold, gl.nodes.keys().cloned().collect::>().into()).unwrap(); + gl.master().initialize(Default::default(), Default::default(), false, threshold, gl.nodes.keys().cloned().collect::>().into()).unwrap(); while let Some((from, to, message)) = gl.take_message() { gl.process_message((from, to, message)).unwrap(); } @@ -1214,7 +1225,7 @@ mod tests { // we need at least 3-of-4 nodes to agree to reach consensus // let's say 1 of 4 nodes disagee - sl.acl_storages[1].prohibit(sl.requester.public().clone(), SessionId::default()); + sl.acl_storages[1].prohibit(public_to_address(sl.requester.public()), SessionId::default()); // then consensus reachable, but single node will disagree while let Some((from, to, message)) = sl.take_message() { @@ -1235,7 +1246,7 @@ mod tests { // we need at least 3-of-4 nodes to agree to reach consensus // let's say 1 of 4 nodes disagee - sl.acl_storages[0].prohibit(sl.requester.public().clone(), SessionId::default()); + sl.acl_storages[0].prohibit(public_to_address(sl.requester.public()), SessionId::default()); // then consensus reachable, but single node will disagree while let Some((from, to, message)) = sl.take_message() { diff --git a/secret_store/src/key_server_cluster/client_sessions/signing_session_schnorr.rs b/secret_store/src/key_server_cluster/client_sessions/signing_session_schnorr.rs index 4eaf072373def4c6d407ccf2b32872160f105126..376eab26b4f66997a2e6983fa8aae834cbad0272 100644 --- a/secret_store/src/key_server_cluster/client_sessions/signing_session_schnorr.rs +++ b/secret_store/src/key_server_cluster/client_sessions/signing_session_schnorr.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -209,6 +209,7 @@ impl SessionImpl { /// Wait for session completion. pub fn wait(&self) -> Result<(Secret, Secret), Error> { Self::wait_session(&self.core.completed, &self.data, None, |data| data.result.clone()) + .expect("wait_session returns Some if called without timeout; qed") } /// Delegate session to other node. @@ -245,7 +246,7 @@ impl SessionImpl { // check if version exists let key_version = match self.core.key_share.as_ref() { None => return Err(Error::InvalidMessage), - Some(key_share) => key_share.version(&version).map_err(|e| Error::KeyStorage(e.into()))?, + Some(key_share) => key_share.version(&version)?, }; let mut data = self.data.lock(); @@ -277,9 +278,9 @@ impl SessionImpl { }), nonce: None, }); - generation_session.initialize(Default::default(), false, 0, vec![self.core.meta.self_node_id.clone()].into_iter().collect::>().into())?; + generation_session.initialize(Default::default(), Default::default(), false, 0, vec![self.core.meta.self_node_id.clone()].into_iter().collect::>().into())?; - debug_assert_eq!(generation_session.state(), GenerationSessionState::WaitingForGenerationConfirmation); + debug_assert_eq!(generation_session.state(), GenerationSessionState::Finished); let joint_public_and_secret = generation_session .joint_public_and_secret() .expect("session key is generated before signature is computed; we are in SignatureComputing state; qed")?; @@ -312,7 +313,7 @@ impl SessionImpl { &SchnorrSigningMessage::SchnorrPartialSignature(ref message) => self.on_partial_signature(sender, message), &SchnorrSigningMessage::SchnorrSigningSessionError(ref message) => - self.process_node_error(Some(&sender), Error::Io(message.error.clone())), + self.process_node_error(Some(&sender), message.error.clone()), &SchnorrSigningMessage::SchnorrSigningSessionCompleted(ref message) => self.on_session_completed(sender, message), &SchnorrSigningMessage::SchnorrSigningSessionDelegation(ref message) => @@ -406,7 +407,7 @@ impl SessionImpl { nonce: None, }); - generation_session.initialize(Default::default(), false, key_share.threshold, consensus_group.into())?; + generation_session.initialize(Default::default(), Default::default(), false, key_share.threshold, consensus_group.into())?; data.generation_session = Some(generation_session); data.state = SessionState::SessionKeyGeneration; @@ -499,8 +500,7 @@ impl SessionImpl { .expect("session key is generated before signature is computed; we are in SignatureComputing state; qed") .joint_public_and_secret() .expect("session key is generated before signature is computed; we are in SignatureComputing state; qed")?; - let key_version = key_share.version(data.version.as_ref().ok_or(Error::InvalidMessage)?) - .map_err(|e| Error::KeyStorage(e.into()))?.hash.clone(); + let key_version = key_share.version(data.version.as_ref().ok_or(Error::InvalidMessage)?)?.hash.clone(); let signing_job = SchnorrSigningJob::new_on_slave(self.core.meta.self_node_id.clone(), key_share.clone(), key_version, joint_public_and_secret.0, joint_public_and_secret.1)?; let signing_transport = self.core.signing_transport(); @@ -508,7 +508,7 @@ impl SessionImpl { id: message.request_id.clone().into(), message_hash: message.message_hash.clone().into(), other_nodes_ids: message.nodes.iter().cloned().map(Into::into).collect(), - }, signing_job, signing_transport) + }, signing_job, signing_transport).map(|_| ()) } /// When partial signature is received. @@ -563,7 +563,7 @@ impl SessionImpl { match { match node { - Some(node) => data.consensus_session.on_node_error(node), + Some(node) => data.consensus_session.on_node_error(node, error.clone()), None => data.consensus_session.on_session_timeout(), } } { @@ -716,6 +716,14 @@ impl Cluster for SessionKeyGenerationTransport { fn nodes(&self) -> BTreeSet { self.cluster.nodes() } + + fn configured_nodes_count(&self) -> usize { + self.cluster.configured_nodes_count() + } + + fn connected_nodes_count(&self) -> usize { + self.cluster.connected_nodes_count() + } } impl SessionCore { @@ -734,9 +742,10 @@ impl SessionCore { Some(key_share) => key_share, }; - let key_version = key_share.version(version).map_err(|e| Error::KeyStorage(e.into()))?.hash.clone(); - let signing_job = SchnorrSigningJob::new_on_master(self.meta.self_node_id.clone(), key_share.clone(), key_version, session_public, session_secret_share, message_hash)?; - consensus_session.disseminate_jobs(signing_job, self.signing_transport(), false) + let key_version = key_share.version(version)?.hash.clone(); + let signing_job = SchnorrSigningJob::new_on_master(self.meta.self_node_id.clone(), key_share.clone(), key_version, + session_public, session_secret_share, message_hash)?; + consensus_session.disseminate_jobs(signing_job, self.signing_transport(), false).map(|_| ()) } } @@ -802,7 +811,7 @@ mod tests { use std::str::FromStr; use std::collections::{BTreeSet, BTreeMap, VecDeque}; use ethereum_types::{Address, H256}; - use ethkey::{self, Random, Generator, Public, Secret, KeyPair}; + use ethkey::{self, Random, Generator, Public, Secret, KeyPair, public_to_address}; use acl_storage::DummyAclStorage; use key_server_cluster::{NodeId, DummyKeyStorage, DocumentKeyShare, DocumentKeyShareVersion, SessionId, Requester, SessionMeta, Error, KeyStorage}; @@ -849,6 +858,8 @@ mod tests { self_node_id: gl_node_id.clone(), master_node_id: master_node_id.clone(), threshold: gl_node.key_storage.get(&session_id).unwrap().unwrap().threshold, + configured_nodes_count: gl.nodes.len(), + connected_nodes_count: gl.nodes.len(), }, access_key: "834cb736f02d9c968dfaf0c37658a1d86ff140554fc8b59c9fdad5a8cf810eec".parse().unwrap(), key_share: Some(gl_node.key_storage.get(&session_id).unwrap().unwrap()), @@ -928,7 +939,7 @@ mod tests { fn prepare_signing_sessions(threshold: usize, num_nodes: usize) -> (KeyGenerationMessageLoop, MessageLoop) { // run key generation sessions let mut gl = KeyGenerationMessageLoop::new(num_nodes); - gl.master().initialize(Default::default(), false, threshold, gl.nodes.keys().cloned().collect::>().into()).unwrap(); + gl.master().initialize(Default::default(), Default::default(), false, threshold, gl.nodes.keys().cloned().collect::>().into()).unwrap(); while let Some((from, to, message)) = gl.take_message() { gl.process_message((from, to, message)).unwrap(); } @@ -969,6 +980,8 @@ mod tests { self_node_id: self_node_id.clone(), master_node_id: self_node_id.clone(), threshold: 0, + configured_nodes_count: 1, + connected_nodes_count: 1, }, access_key: Random.generate().unwrap().secret().clone(), key_share: Some(DocumentKeyShare { @@ -1001,6 +1014,8 @@ mod tests { self_node_id: self_node_id.clone(), master_node_id: self_node_id.clone(), threshold: 0, + configured_nodes_count: 1, + connected_nodes_count: 1, }, access_key: Random.generate().unwrap().secret().clone(), key_share: None, @@ -1023,6 +1038,8 @@ mod tests { self_node_id: self_node_id.clone(), master_node_id: self_node_id.clone(), threshold: 2, + configured_nodes_count: 1, + connected_nodes_count: 1, }, access_key: Random.generate().unwrap().secret().clone(), key_share: Some(DocumentKeyShare { @@ -1114,6 +1131,7 @@ mod tests { message: GenerationMessage::InitializeSession(InitializeSession { session: SessionId::default().into(), session_nonce: 0, + origin: None, author: Address::default().into(), nodes: BTreeMap::new(), is_zero: false, @@ -1157,8 +1175,8 @@ mod tests { // we need at least 2-of-3 nodes to agree to reach consensus // let's say 2 of 3 nodes disagee - sl.acl_storages[1].prohibit(sl.requester.public().clone(), SessionId::default()); - sl.acl_storages[2].prohibit(sl.requester.public().clone(), SessionId::default()); + sl.acl_storages[1].prohibit(public_to_address(sl.requester.public()), SessionId::default()); + sl.acl_storages[2].prohibit(public_to_address(sl.requester.public()), SessionId::default()); // then consensus is unreachable assert_eq!(sl.run_until(|_| false), Err(Error::ConsensusUnreachable)); @@ -1171,7 +1189,7 @@ mod tests { // we need at least 2-of-3 nodes to agree to reach consensus // let's say 1 of 3 nodes disagee - sl.acl_storages[1].prohibit(sl.requester.public().clone(), SessionId::default()); + sl.acl_storages[1].prohibit(public_to_address(sl.requester.public()), SessionId::default()); // then consensus reachable, but single node will disagree while let Some((from, to, message)) = sl.take_message() { @@ -1192,7 +1210,7 @@ mod tests { // we need at least 2-of-3 nodes to agree to reach consensus // let's say 1 of 3 nodes disagee - sl.acl_storages[0].prohibit(sl.requester.public().clone(), SessionId::default()); + sl.acl_storages[0].prohibit(public_to_address(sl.requester.public()), SessionId::default()); // then consensus reachable, but single node will disagree while let Some((from, to, message)) = sl.take_message() { @@ -1271,4 +1289,4 @@ mod tests { _ => unreachable!(), } } -} \ No newline at end of file +} diff --git a/secret_store/src/key_server_cluster/cluster.rs b/secret_store/src/key_server_cluster/cluster.rs index 2dcefea25ad3c14f7cd6d305cefd963b3728a494..b48290a4fc33673ebf39920d56e3a87aa9e2dfca 100644 --- a/secret_store/src/key_server_cluster/cluster.rs +++ b/secret_store/src/key_server_cluster/cluster.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -15,7 +15,7 @@ // along with Parity. If not, see . use std::io; -use std::time; +use std::time::{Duration, Instant}; use std::sync::Arc; use std::collections::{BTreeMap, BTreeSet}; use std::collections::btree_map::Entry; @@ -26,8 +26,8 @@ use parking_lot::{RwLock, Mutex}; use tokio_io::IoFuture; use tokio_core::reactor::{Handle, Remote, Interval}; use tokio_core::net::{TcpListener, TcpStream}; -use ethkey::{Public, KeyPair, Signature, Random, Generator, public_to_address}; -use ethereum_types::H256; +use ethkey::{Public, KeyPair, Signature, Random, Generator}; +use ethereum_types::{Address, H256}; use key_server_cluster::{Error, NodeId, SessionId, Requester, AclStorage, KeyStorage, KeyServerSet, NodeKeyPair}; use key_server_cluster::cluster_sessions::{ClusterSession, AdminSession, ClusterSessions, SessionIdWithSubSession, ClusterSessionsContainer, SERVERS_SET_CHANGE_SESSION_ID, create_cluster_view, AdminSessionCreationData, ClusterSessionsListener}; @@ -53,10 +53,10 @@ const MAINTAIN_INTERVAL: u64 = 10; /// When no messages have been received from node within KEEP_ALIVE_SEND_INTERVAL seconds, /// we must send KeepAlive message to the node to check if it still responds to messages. -const KEEP_ALIVE_SEND_INTERVAL: u64 = 30; +const KEEP_ALIVE_SEND_INTERVAL: Duration = Duration::from_secs(30); /// When no messages have been received from node within KEEP_ALIVE_DISCONNECT_INTERVAL seconds, /// we must treat this node as non-responding && disconnect from it. -const KEEP_ALIVE_DISCONNECT_INTERVAL: u64 = 60; +const KEEP_ALIVE_DISCONNECT_INTERVAL: Duration = Duration::from_secs(60); /// Empty future. pub type BoxedEmptyFuture = Box + Send>; @@ -66,11 +66,11 @@ pub trait ClusterClient: Send + Sync { /// Get cluster state. fn cluster_state(&self) -> ClusterState; /// Start new generation session. - fn new_generation_session(&self, session_id: SessionId, author: Public, threshold: usize) -> Result, Error>; + fn new_generation_session(&self, session_id: SessionId, origin: Option
, author: Address, threshold: usize) -> Result, Error>; /// Start new encryption session. - fn new_encryption_session(&self, session_id: SessionId, requester: Requester, common_point: Public, encrypted_point: Public) -> Result, Error>; + fn new_encryption_session(&self, session_id: SessionId, author: Requester, common_point: Public, encrypted_point: Public) -> Result, Error>; /// Start new decryption session. - fn new_decryption_session(&self, session_id: SessionId, requester: Requester, version: Option, is_shadow_decryption: bool) -> Result, Error>; + fn new_decryption_session(&self, session_id: SessionId, origin: Option
, requester: Requester, version: Option, is_shadow_decryption: bool, is_broadcast_decryption: bool) -> Result, Error>; /// Start new Schnorr signing session. fn new_schnorr_signing_session(&self, session_id: SessionId, requester: Requester, version: Option, message_hash: H256) -> Result, Error>; /// Start new ECDSA session. @@ -82,6 +82,10 @@ pub trait ClusterClient: Send + Sync { /// Listen for new generation sessions. fn add_generation_listener(&self, listener: Arc>); + /// Listen for new decryption sessions. + fn add_decryption_listener(&self, listener: Arc>); + /// Listen for new key version negotiation sessions. + fn add_key_version_negotiation_listener(&self, listener: Arc>>); /// Ask node to make 'faulty' generation sessions. #[cfg(test)] @@ -107,6 +111,10 @@ pub trait Cluster: Send + Sync { fn is_connected(&self, node: &NodeId) -> bool; /// Get a set of connected nodes. fn nodes(&self) -> BTreeSet; + /// Get total count of configured key server nodes (valid at the time of ClusterView creation). + fn configured_nodes_count(&self) -> usize; + /// Get total count of connected key server nodes (valid at the time of ClusterView creation). + fn connected_nodes_count(&self) -> usize; } /// Cluster initialization parameters. @@ -158,6 +166,8 @@ pub struct ClusterClientImpl { /// Network cluster view. It is a communication channel, required in single session. pub struct ClusterView { core: Arc>, + configured_nodes_count: usize, + connected_nodes_count: usize, } /// Cross-thread shareable cluster data. @@ -190,9 +200,10 @@ pub struct ClusterConnections { pub data: RwLock, } -#[derive(Default)] /// Cluster connections data. pub struct ClusterConnectionsData { + /// Is this node isolated from cluster? + pub is_isolated: bool, /// Active key servers set. pub nodes: BTreeMap, /// Active connections to key servers. @@ -220,7 +231,7 @@ pub struct Connection { /// Connection key. key: KeyPair, /// Last message time. - last_message_time: Mutex, + last_message_time: Mutex, } impl ClusterCore { @@ -324,7 +335,7 @@ impl ClusterCore { /// Schedule mainatain procedures. fn schedule_maintain(handle: &Handle, data: Arc) { let d = data.clone(); - let interval: BoxedEmptyFuture = Box::new(Interval::new(time::Duration::new(MAINTAIN_INTERVAL, 0), handle) + let interval: BoxedEmptyFuture = Box::new(Interval::new(Duration::new(MAINTAIN_INTERVAL, 0), handle) .expect("failed to create interval") .and_then(move |_| Ok(ClusterCore::maintain(data.clone()))) .for_each(|_| Ok(())) @@ -374,12 +385,15 @@ impl ClusterCore { fn keep_alive(data: Arc) { data.sessions.sessions_keep_alive(); for connection in data.connections.active_connections() { - let last_message_diff = time::Instant::now() - connection.last_message_time(); - if last_message_diff > time::Duration::from_secs(KEEP_ALIVE_DISCONNECT_INTERVAL) { + let last_message_diff = Instant::now() - connection.last_message_time(); + if last_message_diff > KEEP_ALIVE_DISCONNECT_INTERVAL { + warn!(target: "secretstore_net", "{}: keep alive timeout for node {}", + data.self_key_pair.public(), connection.node_id()); + data.connections.remove(data.clone(), connection.node_id(), connection.is_inbound()); data.sessions.on_connection_timeout(connection.node_id()); } - else if last_message_diff > time::Duration::from_secs(KEEP_ALIVE_SEND_INTERVAL) { + else if last_message_diff > KEEP_ALIVE_SEND_INTERVAL { data.spawn(connection.send_message(Message::Cluster(ClusterMessage::KeepAlive(message::KeepAlive {})))); } } @@ -434,7 +448,7 @@ impl ClusterCore { /// Process single message from the connection. fn process_connection_message(data: Arc, connection: Arc, message: Message) { - connection.set_last_message_time(time::Instant::now()); + connection.set_last_message_time(Instant::now()); trace!(target: "secretstore_net", "{}: received message {} from {}", data.self_key_pair.public(), message, connection.node_id()); // error is ignored as we only process errors on session level match message { @@ -476,12 +490,12 @@ impl ClusterCore { if is_master_node && session.is_finished() { data.sessions.negotiation_sessions.remove(&session.id()); match session.wait() { - Ok((version, master)) => match session.take_continue_action() { - Some(ContinueAction::Decrypt(session, is_shadow_decryption)) => { + Ok(Some((version, master))) => match session.take_continue_action() { + Some(ContinueAction::Decrypt(session, origin, is_shadow_decryption, is_broadcast_decryption)) => { let initialization_error = if data.self_key_pair.public() == &master { - session.initialize(version, is_shadow_decryption, false) + session.initialize(origin, version, is_shadow_decryption, is_broadcast_decryption) } else { - session.delegate(master, version, is_shadow_decryption, false) + session.delegate(master, origin, version, is_shadow_decryption, is_broadcast_decryption) }; if let Err(error) = initialization_error { @@ -515,18 +529,19 @@ impl ClusterCore { }, None => (), }, + Ok(None) => unreachable!("is_master_node; session is finished; negotiation version always finished with result on master; qed"), Err(error) => match session.take_continue_action() { - Some(ContinueAction::Decrypt(session, _)) => { - data.sessions.decryption_sessions.remove(&session.id()); + Some(ContinueAction::Decrypt(session, _, _, _)) => { session.on_session_error(&meta.self_node_id, error); + data.sessions.decryption_sessions.remove(&session.id()); }, Some(ContinueAction::SchnorrSign(session, _)) => { - data.sessions.schnorr_signing_sessions.remove(&session.id()); session.on_session_error(&meta.self_node_id, error); + data.sessions.schnorr_signing_sessions.remove(&session.id()); }, Some(ContinueAction::EcdsaSign(session, _)) => { - data.sessions.ecdsa_signing_sessions.remove(&session.id()); session.on_session_error(&meta.self_node_id, error); + data.sessions.ecdsa_signing_sessions.remove(&session.id()); }, None => (), }, @@ -552,7 +567,7 @@ impl ClusterCore { let is_initialization_message = message.is_initialization_message(); let is_delegation_message = message.is_delegation_message(); match is_initialization_message || is_delegation_message { - false => sessions.get(&session_id, true).ok_or(Error::InvalidSessionId), + false => sessions.get(&session_id, true).ok_or(Error::NoActiveSessionWithId), true => { let creation_data = SC::creation_data_from_message(&message)?; let master = if is_initialization_message { sender.clone() } else { data.self_key_pair.public().clone() }; @@ -645,12 +660,12 @@ impl ClusterCore { impl ClusterConnections { pub fn new(config: &ClusterConfiguration) -> Result { let mut nodes = config.key_server_set.snapshot().current_set; - nodes.remove(config.self_key_pair.public()); + let is_isolated = nodes.remove(config.self_key_pair.public()).is_none(); let trigger: Box = match config.auto_migrate_enabled { false => Box::new(SimpleConnectionTrigger::new(config.key_server_set.clone(), config.self_key_pair.clone(), config.admin_public.clone())), true if config.admin_public.is_none() => Box::new(ConnectionTriggerWithMigration::new(config.key_server_set.clone(), config.self_key_pair.clone())), - true => return Err(Error::Io("secret store admininstrator public key is specified with auto-migration enabled".into())), // TODO [Refac]: Io -> Internal + true => return Err(Error::Internal("secret store admininstrator public key is specified with auto-migration enabled".into())), }; let connector = trigger.servers_set_change_creator_connector(); @@ -660,6 +675,7 @@ impl ClusterConnections { trigger: Mutex::new(trigger), connector: connector, data: RwLock::new(ClusterConnectionsData { + is_isolated: is_isolated, nodes: nodes, connections: BTreeMap::new(), }), @@ -726,8 +742,13 @@ impl ClusterConnections { self.maintain_connection_trigger(maintain_action, data); } - pub fn connected_nodes(&self) -> BTreeSet { - self.data.read().connections.keys().cloned().collect() + pub fn connected_nodes(&self) -> Result, Error> { + let data = self.data.read(); + if data.is_isolated { + return Err(Error::NodeDisconnected); + } + + Ok(data.connections.keys().cloned().collect()) } pub fn active_connections(&self)-> Vec> { @@ -799,7 +820,7 @@ impl Connection { is_inbound: is_inbound, stream: connection.stream, key: connection.key, - last_message_time: Mutex::new(time::Instant::now()), + last_message_time: Mutex::new(Instant::now()), }) } @@ -811,11 +832,11 @@ impl Connection { &self.node_id } - pub fn last_message_time(&self) -> time::Instant { + pub fn last_message_time(&self) -> Instant { *self.last_message_time.lock() } - pub fn set_last_message_time(&self, last_message_time: time::Instant) { + pub fn set_last_message_time(&self, last_message_time: Instant) { *self.last_message_time.lock() = last_message_time; } @@ -833,8 +854,10 @@ impl Connection { } impl ClusterView { - pub fn new(cluster: Arc, nodes: BTreeSet) -> Self { + pub fn new(cluster: Arc, nodes: BTreeSet, configured_nodes_count: usize) -> Self { ClusterView { + configured_nodes_count: configured_nodes_count, + connected_nodes_count: nodes.len(), core: Arc::new(Mutex::new(ClusterViewCore { cluster: cluster, nodes: nodes, @@ -869,6 +892,14 @@ impl Cluster for ClusterView { fn nodes(&self) -> BTreeSet { self.core.lock().nodes.clone() } + + fn configured_nodes_count(&self) -> usize { + self.configured_nodes_count + } + + fn connected_nodes_count(&self) -> usize { + self.connected_nodes_count + } } impl ClusterClientImpl { @@ -879,7 +910,7 @@ impl ClusterClientImpl { } fn create_key_version_negotiation_session(&self, session_id: SessionId) -> Result>, Error> { - let mut connected_nodes = self.data.connections.connected_nodes(); + let mut connected_nodes = self.data.connections.connected_nodes()?; connected_nodes.insert(self.data.self_key_pair.public().clone()); let access_key = Random.generate()?.secret().clone(); @@ -894,6 +925,20 @@ impl ClusterClientImpl { } } } + + fn process_initialization_result, D>(result: Result<(), Error>, session: Arc, sessions: &ClusterSessionsContainer) -> Result, Error> { + match result { + Ok(()) if session.is_finished() => { + sessions.remove(&session.id()); + Ok(session) + }, + Ok(()) => Ok(session), + Err(error) => { + sessions.remove(&session.id()); + Err(error) + }, + } + } } impl ClusterClient for ClusterClientImpl { @@ -901,38 +946,30 @@ impl ClusterClient for ClusterClientImpl { self.data.connections.cluster_state() } - fn new_generation_session(&self, session_id: SessionId, author: Public, threshold: usize) -> Result, Error> { - let mut connected_nodes = self.data.connections.connected_nodes(); + fn new_generation_session(&self, session_id: SessionId, origin: Option
, author: Address, threshold: usize) -> Result, Error> { + let mut connected_nodes = self.data.connections.connected_nodes()?; connected_nodes.insert(self.data.self_key_pair.public().clone()); let cluster = create_cluster_view(&self.data, true)?; let session = self.data.sessions.generation_sessions.insert(cluster, self.data.self_key_pair.public().clone(), session_id, None, false, None)?; - match session.initialize(public_to_address(&author), false, threshold, connected_nodes.into()) { - Ok(()) => Ok(session), - Err(error) => { - self.data.sessions.generation_sessions.remove(&session.id()); - Err(error) - }, - } + Self::process_initialization_result( + session.initialize(origin, author, false, threshold, connected_nodes.into()), + session, &self.data.sessions.generation_sessions) } fn new_encryption_session(&self, session_id: SessionId, requester: Requester, common_point: Public, encrypted_point: Public) -> Result, Error> { - let mut connected_nodes = self.data.connections.connected_nodes(); + let mut connected_nodes = self.data.connections.connected_nodes()?; connected_nodes.insert(self.data.self_key_pair.public().clone()); let cluster = create_cluster_view(&self.data, true)?; let session = self.data.sessions.encryption_sessions.insert(cluster, self.data.self_key_pair.public().clone(), session_id, None, false, None)?; - match session.initialize(requester, common_point, encrypted_point) { - Ok(()) => Ok(session), - Err(error) => { - self.data.sessions.encryption_sessions.remove(&session.id()); - Err(error) - }, - } + Self::process_initialization_result( + session.initialize(requester, common_point, encrypted_point), + session, &self.data.sessions.encryption_sessions) } - fn new_decryption_session(&self, session_id: SessionId, requester: Requester, version: Option, is_shadow_decryption: bool) -> Result, Error> { - let mut connected_nodes = self.data.connections.connected_nodes(); + fn new_decryption_session(&self, session_id: SessionId, origin: Option
, requester: Requester, version: Option, is_shadow_decryption: bool, is_broadcast_decryption: bool) -> Result, Error> { + let mut connected_nodes = self.data.connections.connected_nodes()?; connected_nodes.insert(self.data.self_key_pair.public().clone()); let access_key = Random.generate()?.secret().clone(); @@ -942,27 +979,23 @@ impl ClusterClient for ClusterClientImpl { session_id.clone(), None, false, Some(requester))?; let initialization_result = match version { - Some(version) => session.initialize(version, is_shadow_decryption, false), + Some(version) => session.initialize(origin, version, is_shadow_decryption, is_broadcast_decryption), None => { self.create_key_version_negotiation_session(session_id.id.clone()) .map(|version_session| { - version_session.set_continue_action(ContinueAction::Decrypt(session.clone(), is_shadow_decryption)); + version_session.set_continue_action(ContinueAction::Decrypt(session.clone(), origin, is_shadow_decryption, is_broadcast_decryption)); ClusterCore::try_continue_session(&self.data, Some(version_session)); }) }, }; - match initialization_result { - Ok(()) => Ok(session), - Err(error) => { - self.data.sessions.decryption_sessions.remove(&session.id()); - Err(error) - }, - } + Self::process_initialization_result( + initialization_result, + session, &self.data.sessions.decryption_sessions) } fn new_schnorr_signing_session(&self, session_id: SessionId, requester: Requester, version: Option, message_hash: H256) -> Result, Error> { - let mut connected_nodes = self.data.connections.connected_nodes(); + let mut connected_nodes = self.data.connections.connected_nodes()?; connected_nodes.insert(self.data.self_key_pair.public().clone()); let access_key = Random.generate()?.secret().clone(); @@ -981,17 +1014,13 @@ impl ClusterClient for ClusterClientImpl { }, }; - match initialization_result { - Ok(()) => Ok(session), - Err(error) => { - self.data.sessions.schnorr_signing_sessions.remove(&session.id()); - Err(error) - }, - } + Self::process_initialization_result( + initialization_result, + session, &self.data.sessions.schnorr_signing_sessions) } fn new_ecdsa_signing_session(&self, session_id: SessionId, requester: Requester, version: Option, message_hash: H256) -> Result, Error> { - let mut connected_nodes = self.data.connections.connected_nodes(); + let mut connected_nodes = self.data.connections.connected_nodes()?; connected_nodes.insert(self.data.self_key_pair.public().clone()); let access_key = Random.generate()?.secret().clone(); @@ -1010,13 +1039,9 @@ impl ClusterClient for ClusterClientImpl { }, }; - match initialization_result { - Ok(()) => Ok(session), - Err(error) => { - self.data.sessions.ecdsa_signing_sessions.remove(&session.id()); - Err(error) - }, - } + Self::process_initialization_result( + initialization_result, + session, &self.data.sessions.ecdsa_signing_sessions) } fn new_key_version_negotiation_session(&self, session_id: SessionId) -> Result>, Error> { @@ -1025,7 +1050,7 @@ impl ClusterClient for ClusterClientImpl { } fn new_servers_set_change_session(&self, session_id: Option, migration_id: Option, new_nodes_set: BTreeSet, old_set_signature: Signature, new_set_signature: Signature) -> Result, Error> { - let mut connected_nodes = self.data.connections.connected_nodes(); + let mut connected_nodes = self.data.connections.connected_nodes()?; connected_nodes.insert(self.data.self_key_pair.public().clone()); let session_id = match session_id { @@ -1040,22 +1065,27 @@ impl ClusterClient for ClusterClientImpl { let initialization_result = session.as_servers_set_change().expect("servers set change session is created; qed") .initialize(new_nodes_set, old_set_signature, new_set_signature); - match initialization_result { - Ok(()) => { - self.data.connections.servers_set_change_creator_connector().set_key_servers_set_change_session(session.clone()); - Ok(session) - }, - Err(error) => { - self.data.sessions.admin_sessions.remove(&session.id()); - Err(error) - }, + if initialization_result.is_ok() { + self.data.connections.servers_set_change_creator_connector().set_key_servers_set_change_session(session.clone()); } + + Self::process_initialization_result( + initialization_result, + session, &self.data.sessions.admin_sessions) } fn add_generation_listener(&self, listener: Arc>) { self.data.sessions.generation_sessions.add_listener(listener); } + fn add_decryption_listener(&self, listener: Arc>) { + self.data.sessions.decryption_sessions.add_listener(listener); + } + + fn add_key_version_negotiation_listener(&self, listener: Arc>>) { + self.data.sessions.negotiation_sessions.add_listener(listener); + } + #[cfg(test)] fn connect(&self) { ClusterCore::connect_disconnected_nodes(self.data.clone()); @@ -1085,11 +1115,12 @@ fn make_socket_address(address: &str, port: u16) -> Result { #[cfg(test)] pub mod tests { use std::sync::Arc; - use std::time; + use std::sync::atomic::{AtomicUsize, Ordering}; + use std::time::{Duration, Instant}; use std::collections::{BTreeSet, VecDeque}; use parking_lot::Mutex; use tokio_core::reactor::Core; - use ethereum_types::H256; + use ethereum_types::{Address, H256}; use ethkey::{Random, Generator, Public, Signature, sign}; use key_server_cluster::{NodeId, SessionId, Requester, Error, DummyAclStorage, DummyKeyStorage, MapKeyServerSet, PlainNodeKeyPair, KeyStorage}; @@ -1104,8 +1135,12 @@ pub mod tests { use key_server_cluster::key_version_negotiation_session::{SessionImpl as KeyVersionNegotiationSession, IsolatedSessionTransport as KeyVersionNegotiationSessionTransport}; + const TIMEOUT: Duration = Duration::from_millis(300); + #[derive(Default)] - pub struct DummyClusterClient; + pub struct DummyClusterClient { + pub generation_requests_count: AtomicUsize, + } #[derive(Debug)] pub struct DummyCluster { @@ -1121,15 +1156,21 @@ pub mod tests { impl ClusterClient for DummyClusterClient { fn cluster_state(&self) -> ClusterState { unimplemented!("test-only") } - fn new_generation_session(&self, _session_id: SessionId, _author: Public, _threshold: usize) -> Result, Error> { unimplemented!("test-only") } + fn new_generation_session(&self, _session_id: SessionId, _origin: Option
, _author: Address, _threshold: usize) -> Result, Error> { + self.generation_requests_count.fetch_add(1, Ordering::Relaxed); + Err(Error::Internal("test-error".into())) + } fn new_encryption_session(&self, _session_id: SessionId, _requester: Requester, _common_point: Public, _encrypted_point: Public) -> Result, Error> { unimplemented!("test-only") } - fn new_decryption_session(&self, _session_id: SessionId, _requester: Requester, _version: Option, _is_shadow_decryption: bool) -> Result, Error> { unimplemented!("test-only") } + fn new_decryption_session(&self, _session_id: SessionId, _origin: Option
, _requester: Requester, _version: Option, _is_shadow_decryption: bool, _is_broadcast_session: bool) -> Result, Error> { unimplemented!("test-only") } fn new_schnorr_signing_session(&self, _session_id: SessionId, _requester: Requester, _version: Option, _message_hash: H256) -> Result, Error> { unimplemented!("test-only") } fn new_ecdsa_signing_session(&self, _session_id: SessionId, _requester: Requester, _version: Option, _message_hash: H256) -> Result, Error> { unimplemented!("test-only") } + fn new_key_version_negotiation_session(&self, _session_id: SessionId) -> Result>, Error> { unimplemented!("test-only") } fn new_servers_set_change_session(&self, _session_id: Option, _migration_id: Option, _new_nodes_set: BTreeSet, _old_set_signature: Signature, _new_set_signature: Signature) -> Result, Error> { unimplemented!("test-only") } fn add_generation_listener(&self, _listener: Arc>) {} + fn add_decryption_listener(&self, _listener: Arc>) {} + fn add_key_version_negotiation_listener(&self, _listener: Arc>>) {} fn make_faulty_generation_sessions(&self) { unimplemented!("test-only") } fn generation_session(&self, _session_id: &SessionId) -> Option> { unimplemented!("test-only") } @@ -1190,17 +1231,25 @@ pub mod tests { fn nodes(&self) -> BTreeSet { self.data.lock().nodes.iter().cloned().collect() } + + fn configured_nodes_count(&self) -> usize { + self.data.lock().nodes.len() + } + + fn connected_nodes_count(&self) -> usize { + self.data.lock().nodes.len() + } } - pub fn loop_until(core: &mut Core, timeout: time::Duration, predicate: F) where F: Fn() -> bool { - let start = time::Instant::now(); + pub fn loop_until(core: &mut Core, timeout: Duration, predicate: F) where F: Fn() -> bool { + let start = Instant::now(); loop { - core.turn(Some(time::Duration::from_millis(1))); + core.turn(Some(Duration::from_millis(1))); if predicate() { break; } - if time::Instant::now() - start > timeout { + if Instant::now() - start > timeout { panic!("no result in {:?}", timeout); } } @@ -1218,7 +1267,7 @@ pub mod tests { threads: 1, self_key_pair: Arc::new(PlainNodeKeyPair::new(key_pairs[i].clone())), listen_address: ("127.0.0.1".to_owned(), ports_begin + i as u16), - key_server_set: Arc::new(MapKeyServerSet::new(key_pairs.iter().enumerate() + key_server_set: Arc::new(MapKeyServerSet::new(false, key_pairs.iter().enumerate() .map(|(j, kp)| (kp.public().clone(), format!("127.0.0.1:{}", ports_begin + j as u16).parse().unwrap())) .collect())), allow_connecting_to_higher_nodes: false, @@ -1248,7 +1297,7 @@ pub mod tests { let mut core = Core::new().unwrap(); let clusters = make_clusters(&core, 6010, 3); run_clusters(&clusters); - loop_until(&mut core, time::Duration::from_millis(300), || clusters.iter().all(all_connections_established)); + loop_until(&mut core, TIMEOUT, || clusters.iter().all(all_connections_established)); } #[test] @@ -1256,7 +1305,7 @@ pub mod tests { let core = Core::new().unwrap(); let clusters = make_clusters(&core, 6013, 3); clusters[0].run().unwrap(); - match clusters[0].client().new_generation_session(SessionId::default(), Public::default(), 1) { + match clusters[0].client().new_generation_session(SessionId::default(), Default::default(), Default::default(), 1) { Err(Error::NodeDisconnected) => (), Err(e) => panic!("unexpected error {:?}", e), _ => panic!("unexpected success"), @@ -1269,14 +1318,14 @@ pub mod tests { let mut core = Core::new().unwrap(); let clusters = make_clusters(&core, 6016, 3); run_clusters(&clusters); - loop_until(&mut core, time::Duration::from_millis(300), || clusters.iter().all(all_connections_established)); + loop_until(&mut core, TIMEOUT, || clusters.iter().all(all_connections_established)); // ask one of nodes to produce faulty generation sessions clusters[1].client().make_faulty_generation_sessions(); // start && wait for generation session to fail - let session = clusters[0].client().new_generation_session(SessionId::default(), Public::default(), 1).unwrap(); - loop_until(&mut core, time::Duration::from_millis(300), || session.joint_public_and_secret().is_some() + let session = clusters[0].client().new_generation_session(SessionId::default(), Default::default(), Default::default(), 1).unwrap(); + loop_until(&mut core, TIMEOUT, || session.joint_public_and_secret().is_some() && clusters[0].client().generation_session(&SessionId::default()).is_none()); assert!(session.joint_public_and_secret().unwrap().is_err()); @@ -1285,7 +1334,7 @@ pub mod tests { if let Some(session) = clusters[i].client().generation_session(&SessionId::default()) { // wait for both session completion && session removal (session completion event is fired // before session is removed from its own container by cluster) - loop_until(&mut core, time::Duration::from_millis(300), || session.joint_public_and_secret().is_some() + loop_until(&mut core, TIMEOUT, || session.joint_public_and_secret().is_some() && clusters[i].client().generation_session(&SessionId::default()).is_none()); assert!(session.joint_public_and_secret().unwrap().is_err()); } @@ -1298,14 +1347,14 @@ pub mod tests { let mut core = Core::new().unwrap(); let clusters = make_clusters(&core, 6025, 3); run_clusters(&clusters); - loop_until(&mut core, time::Duration::from_millis(300), || clusters.iter().all(all_connections_established)); + loop_until(&mut core, TIMEOUT, || clusters.iter().all(all_connections_established)); // ask one of nodes to produce faulty generation sessions clusters[0].client().make_faulty_generation_sessions(); // start && wait for generation session to fail - let session = clusters[0].client().new_generation_session(SessionId::default(), Public::default(), 1).unwrap(); - loop_until(&mut core, time::Duration::from_millis(300), || session.joint_public_and_secret().is_some() + let session = clusters[0].client().new_generation_session(SessionId::default(), Default::default(), Default::default(), 1).unwrap(); + loop_until(&mut core, TIMEOUT, || session.joint_public_and_secret().is_some() && clusters[0].client().generation_session(&SessionId::default()).is_none()); assert!(session.joint_public_and_secret().unwrap().is_err()); @@ -1314,7 +1363,7 @@ pub mod tests { if let Some(session) = clusters[i].client().generation_session(&SessionId::default()) { // wait for both session completion && session removal (session completion event is fired // before session is removed from its own container by cluster) - loop_until(&mut core, time::Duration::from_millis(300), || session.joint_public_and_secret().is_some() + loop_until(&mut core, TIMEOUT, || session.joint_public_and_secret().is_some() && clusters[i].client().generation_session(&SessionId::default()).is_none()); assert!(session.joint_public_and_secret().unwrap().is_err()); } @@ -1327,11 +1376,11 @@ pub mod tests { let mut core = Core::new().unwrap(); let clusters = make_clusters(&core, 6019, 3); run_clusters(&clusters); - loop_until(&mut core, time::Duration::from_millis(300), || clusters.iter().all(all_connections_established)); + loop_until(&mut core, TIMEOUT, || clusters.iter().all(all_connections_established)); // start && wait for generation session to complete - let session = clusters[0].client().new_generation_session(SessionId::default(), Public::default(), 1).unwrap(); - loop_until(&mut core, time::Duration::from_millis(300), || (session.state() == GenerationSessionState::Finished + let session = clusters[0].client().new_generation_session(SessionId::default(), Default::default(), Default::default(), 1).unwrap(); + loop_until(&mut core, TIMEOUT, || (session.state() == GenerationSessionState::Finished || session.state() == GenerationSessionState::Failed) && clusters[0].client().generation_session(&SessionId::default()).is_none()); assert!(session.joint_public_and_secret().unwrap().is_ok()); @@ -1339,7 +1388,7 @@ pub mod tests { // check that session is either removed from all nodes, or nonexistent (already removed) for i in 1..3 { if let Some(session) = clusters[i].client().generation_session(&SessionId::default()) { - loop_until(&mut core, time::Duration::from_millis(300), || (session.state() == GenerationSessionState::Finished + loop_until(&mut core, TIMEOUT, || (session.state() == GenerationSessionState::Finished || session.state() == GenerationSessionState::Failed) && clusters[i].client().generation_session(&SessionId::default()).is_none()); assert!(session.joint_public_and_secret().unwrap().is_err()); @@ -1352,17 +1401,17 @@ pub mod tests { let mut core = Core::new().unwrap(); let clusters = make_clusters(&core, 6022, 3); run_clusters(&clusters); - loop_until(&mut core, time::Duration::from_millis(300), || clusters.iter().all(all_connections_established)); + loop_until(&mut core, TIMEOUT, || clusters.iter().all(all_connections_established)); // generation session { // try to start generation session => fail in initialization - assert_eq!(clusters[0].client().new_generation_session(SessionId::default(), Public::default(), 100).map(|_| ()), - Err(Error::InvalidThreshold)); + assert_eq!(clusters[0].client().new_generation_session(SessionId::default(), Default::default(), Default::default(), 100).map(|_| ()), + Err(Error::NotEnoughNodesForThreshold)); // try to start generation session => fails in initialization - assert_eq!(clusters[0].client().new_generation_session(SessionId::default(), Public::default(), 100).map(|_| ()), - Err(Error::InvalidThreshold)); + assert_eq!(clusters[0].client().new_generation_session(SessionId::default(), Default::default(), Default::default(), 100).map(|_| ()), + Err(Error::NotEnoughNodesForThreshold)); assert!(clusters[0].data.sessions.generation_sessions.is_empty()); } @@ -1370,11 +1419,11 @@ pub mod tests { // decryption session { // try to start decryption session => fails in initialization - assert_eq!(clusters[0].client().new_decryption_session(Default::default(), Default::default(), Some(Default::default()), false).map(|_| ()), + assert_eq!(clusters[0].client().new_decryption_session(Default::default(), Default::default(), Default::default(), Some(Default::default()), false, false).map(|_| ()), Err(Error::InvalidMessage)); // try to start generation session => fails in initialization - assert_eq!(clusters[0].client().new_decryption_session(Default::default(), Default::default(), Some(Default::default()), false).map(|_| ()), + assert_eq!(clusters[0].client().new_decryption_session(Default::default(), Default::default(), Default::default(), Some(Default::default()), false, false).map(|_| ()), Err(Error::InvalidMessage)); assert!(clusters[0].data.sessions.decryption_sessions.is_empty()); @@ -1388,11 +1437,11 @@ pub mod tests { let mut core = Core::new().unwrap(); let clusters = make_clusters(&core, 6028, 3); run_clusters(&clusters); - loop_until(&mut core, time::Duration::from_millis(300), || clusters.iter().all(all_connections_established)); + loop_until(&mut core, TIMEOUT, || clusters.iter().all(all_connections_established)); // start && wait for generation session to complete - let session = clusters[0].client().new_generation_session(SessionId::default(), Public::default(), 1).unwrap(); - loop_until(&mut core, time::Duration::from_millis(300), || (session.state() == GenerationSessionState::Finished + let session = clusters[0].client().new_generation_session(SessionId::default(), Default::default(), Default::default(), 1).unwrap(); + loop_until(&mut core, TIMEOUT, || (session.state() == GenerationSessionState::Finished || session.state() == GenerationSessionState::Failed) && clusters[0].client().generation_session(&SessionId::default()).is_none()); assert!(session.joint_public_and_secret().unwrap().is_ok()); @@ -1406,7 +1455,7 @@ pub mod tests { let session0 = clusters[0].client().new_schnorr_signing_session(Default::default(), signature.into(), None, Default::default()).unwrap(); let session = clusters[0].data.sessions.schnorr_signing_sessions.first().unwrap(); - loop_until(&mut core, time::Duration::from_millis(300), || session.is_finished() && (0..3).all(|i| + loop_until(&mut core, TIMEOUT, || session.is_finished() && (0..3).all(|i| clusters[i].data.sessions.schnorr_signing_sessions.is_empty())); session0.wait().unwrap(); @@ -1415,7 +1464,7 @@ pub mod tests { let session2 = clusters[2].client().new_schnorr_signing_session(Default::default(), signature.into(), None, Default::default()).unwrap(); let session = clusters[2].data.sessions.schnorr_signing_sessions.first().unwrap(); - loop_until(&mut core, time::Duration::from_millis(300), || session.is_finished() && (0..3).all(|i| + loop_until(&mut core, TIMEOUT, || session.is_finished() && (0..3).all(|i| clusters[i].data.sessions.schnorr_signing_sessions.is_empty())); session2.wait().unwrap(); @@ -1427,7 +1476,7 @@ pub mod tests { let session1 = clusters[0].client().new_schnorr_signing_session(Default::default(), signature.into(), None, Default::default()).unwrap(); let session = clusters[0].data.sessions.schnorr_signing_sessions.first().unwrap(); - loop_until(&mut core, time::Duration::from_millis(300), || session.is_finished()); + loop_until(&mut core, TIMEOUT, || session.is_finished()); session1.wait().unwrap_err(); } @@ -1437,11 +1486,11 @@ pub mod tests { let mut core = Core::new().unwrap(); let clusters = make_clusters(&core, 6041, 4); run_clusters(&clusters); - loop_until(&mut core, time::Duration::from_millis(300), || clusters.iter().all(all_connections_established)); + loop_until(&mut core, TIMEOUT, || clusters.iter().all(all_connections_established)); // start && wait for generation session to complete - let session = clusters[0].client().new_generation_session(SessionId::default(), Public::default(), 1).unwrap(); - loop_until(&mut core, time::Duration::from_millis(300), || (session.state() == GenerationSessionState::Finished + let session = clusters[0].client().new_generation_session(SessionId::default(), Default::default(), Default::default(), 1).unwrap(); + loop_until(&mut core, TIMEOUT, || (session.state() == GenerationSessionState::Finished || session.state() == GenerationSessionState::Failed) && clusters[0].client().generation_session(&SessionId::default()).is_none()); assert!(session.joint_public_and_secret().unwrap().is_ok()); @@ -1455,7 +1504,7 @@ pub mod tests { let session0 = clusters[0].client().new_ecdsa_signing_session(Default::default(), signature.into(), None, H256::random()).unwrap(); let session = clusters[0].data.sessions.ecdsa_signing_sessions.first().unwrap(); - loop_until(&mut core, time::Duration::from_millis(1000), || session.is_finished() && (0..3).all(|i| + loop_until(&mut core, Duration::from_millis(1000), || session.is_finished() && (0..3).all(|i| clusters[i].data.sessions.ecdsa_signing_sessions.is_empty())); session0.wait().unwrap(); @@ -1463,7 +1512,7 @@ pub mod tests { let signature = sign(Random.generate().unwrap().secret(), &Default::default()).unwrap(); let session2 = clusters[2].client().new_ecdsa_signing_session(Default::default(), signature.into(), None, H256::random()).unwrap(); let session = clusters[2].data.sessions.ecdsa_signing_sessions.first().unwrap(); - loop_until(&mut core, time::Duration::from_millis(1000), || session.is_finished() && (0..3).all(|i| + loop_until(&mut core, Duration::from_millis(1000), || session.is_finished() && (0..3).all(|i| clusters[i].data.sessions.ecdsa_signing_sessions.is_empty())); session2.wait().unwrap(); @@ -1474,7 +1523,7 @@ pub mod tests { let signature = sign(Random.generate().unwrap().secret(), &Default::default()).unwrap(); let session1 = clusters[0].client().new_ecdsa_signing_session(Default::default(), signature.into(), None, H256::random()).unwrap(); let session = clusters[0].data.sessions.ecdsa_signing_sessions.first().unwrap(); - loop_until(&mut core, time::Duration::from_millis(1000), || session.is_finished()); + loop_until(&mut core, Duration::from_millis(1000), || session.is_finished()); session1.wait().unwrap_err(); } } diff --git a/secret_store/src/key_server_cluster/cluster_sessions.rs b/secret_store/src/key_server_cluster/cluster_sessions.rs index 86e38a879a8f4c32f340455a8fb55e79d3ac55f0..b34485638e6de3f43df973e386ba33114b4982ef 100644 --- a/secret_store/src/key_server_cluster/cluster_sessions.rs +++ b/secret_store/src/key_server_cluster/cluster_sessions.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use std::time; +use std::time::{Duration, Instant}; use std::sync::{Arc, Weak}; use std::sync::atomic::AtomicBool; use std::collections::{VecDeque, BTreeMap, BTreeSet}; @@ -43,9 +43,9 @@ use key_server_cluster::cluster_sessions_creator::{GenerationSessionCreator, Enc /// we must treat this session as stalled && finish it with an error. /// This timeout is for cases when node is responding to KeepAlive messages, but intentionally ignores /// session messages. -const SESSION_TIMEOUT_INTERVAL: u64 = 60; +const SESSION_TIMEOUT_INTERVAL: Duration = Duration::from_secs(60); /// Interval to send session-level KeepAlive-messages. -const SESSION_KEEP_ALIVE_INTERVAL: u64 = 30; +const SESSION_KEEP_ALIVE_INTERVAL: Duration = Duration::from_secs(30); lazy_static! { /// Servers set change session id (there could be at most 1 session => hardcoded id). @@ -84,10 +84,10 @@ pub trait ClusterSession { fn on_message(&self, sender: &NodeId, message: &Message) -> Result<(), Error>; /// 'Wait for session completion' helper. - fn wait_session Option>>(completion_event: &Condvar, session_data: &Mutex, timeout: Option, result_reader: F) -> Result { + fn wait_session Option>>(completion_event: &Condvar, session_data: &Mutex, timeout: Option, result_reader: F) -> Option> { let mut locked_data = session_data.lock(); match result_reader(&locked_data) { - Some(result) => result, + Some(result) => Some(result), None => { match timeout { None => completion_event.wait(&mut locked_data), @@ -97,7 +97,6 @@ pub trait ClusterSession { } result_reader(&locked_data) - .expect("waited for completion; completion is only signaled when result.is_some(); qed") }, } } @@ -170,9 +169,9 @@ pub struct QueuedSession { /// Cluster view. pub cluster_view: Arc, /// Last keep alive time. - pub last_keep_alive_time: time::Instant, + pub last_keep_alive_time: Instant, /// Last received message time. - pub last_message_time: time::Instant, + pub last_message_time: Instant, /// Generation session. pub session: Arc, /// Messages queue. @@ -291,7 +290,7 @@ impl ClusterSessionsContainer where S: ClusterSession, SC: C sessions.get_mut(session_id) .map(|s| { if update_last_message_time { - s.last_message_time = time::Instant::now(); + s.last_message_time = Instant::now(); } s.session.clone() }) @@ -319,8 +318,8 @@ impl ClusterSessionsContainer where S: ClusterSession, SC: C let queued_session = QueuedSession { master: master, cluster_view: cluster, - last_keep_alive_time: time::Instant::now(), - last_message_time: time::Instant::now(), + last_keep_alive_time: Instant::now(), + last_message_time: Instant::now(), session: session.clone(), queue: VecDeque::new(), }; @@ -331,10 +330,7 @@ impl ClusterSessionsContainer where S: ClusterSession, SC: C } pub fn remove(&self, session_id: &S::Id) { - if let Some(session) = self.sessions.write().remove(session_id) { - self.container_state.lock().on_session_completed(); - self.notify_listeners(|l| l.on_session_removed(session.session.clone())); - } + self.do_remove(session_id, &mut *self.sessions.write()); } pub fn enqueue_message(&self, session_id: &S::Id, sender: NodeId, message: Message, is_queued_message: bool) { @@ -353,7 +349,7 @@ impl ClusterSessionsContainer where S: ClusterSession, SC: C for sid in sessions.keys().cloned().collect::>() { let remove_session = { let session = sessions.get(&sid).expect("enumerating only existing sessions; qed"); - if time::Instant::now() - session.last_message_time > time::Duration::from_secs(SESSION_TIMEOUT_INTERVAL) { + if Instant::now() - session.last_message_time > SESSION_TIMEOUT_INTERVAL { session.session.on_session_timeout(); session.session.is_finished() } else { @@ -362,7 +358,7 @@ impl ClusterSessionsContainer where S: ClusterSession, SC: C }; if remove_session { - sessions.remove(&sid); + self.do_remove(&sid, &mut *sessions); } } } @@ -375,12 +371,20 @@ impl ClusterSessionsContainer where S: ClusterSession, SC: C session.session.on_node_timeout(node_id); session.session.is_finished() }; + if remove_session { - sessions.remove(&sid); + self.do_remove(&sid, &mut *sessions); } } } + fn do_remove(&self, session_id: &S::Id, sessions: &mut BTreeMap>) { + if let Some(session) = sessions.remove(session_id) { + self.container_state.lock().on_session_completed(); + self.notify_listeners(|l| l.on_session_removed(session.session.clone())); + } + } + fn notify_listeners) -> ()>(&self, callback: F) { let mut listeners = self.listeners.lock(); let mut listener_index = 0; @@ -401,8 +405,8 @@ impl ClusterSessionsContainer where S: ClusterSession, SC: C impl ClusterSessionsContainer where S: ClusterSession, SC: ClusterSessionCreator, SessionId: From { pub fn send_keep_alive(&self, session_id: &S::Id, self_node_id: &NodeId) { if let Some(session) = self.sessions.write().get_mut(session_id) { - let now = time::Instant::now(); - if self_node_id == &session.master && now - session.last_keep_alive_time > time::Duration::from_secs(SESSION_KEEP_ALIVE_INTERVAL) { + let now = Instant::now(); + if self_node_id == &session.master && now - session.last_keep_alive_time > SESSION_KEEP_ALIVE_INTERVAL { session.last_keep_alive_time = now; // since we send KeepAlive message to prevent nodes from disconnecting // && worst thing that can happen if node is disconnected is that session is failed @@ -416,7 +420,7 @@ impl ClusterSessionsContainer where S: ClusterSession, SC: C pub fn on_keep_alive(&self, session_id: &S::Id, sender: &NodeId) { if let Some(session) = self.sessions.write().get_mut(session_id) { - let now = time::Instant::now(); + let now = Instant::now(); // we only accept keep alive from master node of ServersSetChange session if sender == &session.master { session.last_keep_alive_time = now; @@ -548,27 +552,32 @@ impl ClusterSession for AdminSession { } } pub fn create_cluster_view(data: &Arc, requires_all_connections: bool) -> Result, Error> { + let disconnected_nodes_count = data.connections.disconnected_nodes().len(); if requires_all_connections { - if !data.connections.disconnected_nodes().is_empty() { + if disconnected_nodes_count != 0 { return Err(Error::NodeDisconnected); } } - let mut connected_nodes = data.connections.connected_nodes(); + let mut connected_nodes = data.connections.connected_nodes()?; connected_nodes.insert(data.self_key_pair.public().clone()); - Ok(Arc::new(ClusterView::new(data.clone(), connected_nodes))) + let connected_nodes_count = connected_nodes.len(); + Ok(Arc::new(ClusterView::new(data.clone(), connected_nodes, connected_nodes_count + disconnected_nodes_count))) } #[cfg(test)] mod tests { use std::sync::Arc; + use std::sync::atomic::{AtomicUsize, Ordering}; use ethkey::{Random, Generator}; use key_server_cluster::{Error, DummyAclStorage, DummyKeyStorage, MapKeyServerSet, PlainNodeKeyPair}; use key_server_cluster::cluster::ClusterConfiguration; use key_server_cluster::connection_trigger::SimpleServersSetChangeSessionCreatorConnector; use key_server_cluster::cluster::tests::DummyCluster; - use super::{ClusterSessions, AdminSessionCreationData}; + use key_server_cluster::generation_session::{SessionImpl as GenerationSession}; + use super::{ClusterSessions, AdminSessionCreationData, ClusterSessionsListener, + ClusterSessionsContainerState, SESSION_TIMEOUT_INTERVAL}; pub fn make_cluster_sessions() -> ClusterSessions { let key_pair = Random.generate().unwrap(); @@ -576,7 +585,7 @@ mod tests { threads: 1, self_key_pair: Arc::new(PlainNodeKeyPair::new(key_pair.clone())), listen_address: ("127.0.0.1".to_owned(), 100_u16), - key_server_set: Arc::new(MapKeyServerSet::new(vec![(key_pair.public().clone(), format!("127.0.0.1:{}", 100).parse().unwrap())].into_iter().collect())), + key_server_set: Arc::new(MapKeyServerSet::new(false, vec![(key_pair.public().clone(), format!("127.0.0.1:{}", 100).parse().unwrap())].into_iter().collect())), allow_connecting_to_higher_nodes: false, key_storage: Arc::new(DummyKeyStorage::default()), acl_storage: Arc::new(DummyAclStorage::default()), @@ -610,4 +619,72 @@ mod tests { Ok(_) => unreachable!("OK"), } } + + #[test] + fn session_listener_works() { + #[derive(Default)] + struct GenerationSessionListener { + inserted: AtomicUsize, + removed: AtomicUsize, + } + + impl ClusterSessionsListener for GenerationSessionListener { + fn on_session_inserted(&self, _session: Arc) { + self.inserted.fetch_add(1, Ordering::Relaxed); + } + + fn on_session_removed(&self, _session: Arc) { + self.removed.fetch_add(1, Ordering::Relaxed); + } + } + + let listener = Arc::new(GenerationSessionListener::default()); + let sessions = make_cluster_sessions(); + sessions.generation_sessions.add_listener(listener.clone()); + + sessions.generation_sessions.insert(Arc::new(DummyCluster::new(Default::default())), Default::default(), Default::default(), None, false, None).unwrap(); + assert_eq!(listener.inserted.load(Ordering::Relaxed), 1); + assert_eq!(listener.removed.load(Ordering::Relaxed), 0); + + sessions.generation_sessions.remove(&Default::default()); + assert_eq!(listener.inserted.load(Ordering::Relaxed), 1); + assert_eq!(listener.removed.load(Ordering::Relaxed), 1); + } + + #[test] + fn last_session_removal_sets_container_state_to_idle() { + let sessions = make_cluster_sessions(); + + sessions.generation_sessions.insert(Arc::new(DummyCluster::new(Default::default())), Default::default(), Default::default(), None, false, None).unwrap(); + assert_eq!(*sessions.generation_sessions.container_state.lock(), ClusterSessionsContainerState::Active(1)); + + sessions.generation_sessions.remove(&Default::default()); + assert_eq!(*sessions.generation_sessions.container_state.lock(), ClusterSessionsContainerState::Idle); + } + + #[test] + fn last_session_removal_by_timeout_sets_container_state_to_idle() { + let sessions = make_cluster_sessions(); + + sessions.generation_sessions.insert(Arc::new(DummyCluster::new(Default::default())), Default::default(), Default::default(), None, false, None).unwrap(); + assert_eq!(*sessions.generation_sessions.container_state.lock(), ClusterSessionsContainerState::Active(1)); + + sessions.generation_sessions.sessions.write().get_mut(&Default::default()).unwrap().last_message_time -= SESSION_TIMEOUT_INTERVAL * 2; + + sessions.generation_sessions.stop_stalled_sessions(); + assert_eq!(sessions.generation_sessions.sessions.read().len(), 0); + assert_eq!(*sessions.generation_sessions.container_state.lock(), ClusterSessionsContainerState::Idle); + } + + #[test] + fn last_session_removal_by_node_timeout_sets_container_state_to_idle() { + let sessions = make_cluster_sessions(); + + sessions.generation_sessions.insert(Arc::new(DummyCluster::new(Default::default())), Default::default(), Default::default(), None, false, None).unwrap(); + assert_eq!(*sessions.generation_sessions.container_state.lock(), ClusterSessionsContainerState::Active(1)); + + sessions.generation_sessions.on_connection_timeout(&Default::default()); + assert_eq!(sessions.generation_sessions.sessions.read().len(), 0); + assert_eq!(*sessions.generation_sessions.container_state.lock(), ClusterSessionsContainerState::Idle); + } } diff --git a/secret_store/src/key_server_cluster/cluster_sessions_creator.rs b/secret_store/src/key_server_cluster/cluster_sessions_creator.rs index 86e40853e2203cb80c2a1814f9563bfce857d47b..15192542b5cdccddd30af1fd866cda3db22cc3ae 100644 --- a/secret_store/src/key_server_cluster/cluster_sessions_creator.rs +++ b/secret_store/src/key_server_cluster/cluster_sessions_creator.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -115,7 +115,7 @@ impl SessionCreatorCore { /// Read key share && remove disconnected nodes. fn read_key_share(&self, key_id: &SessionId) -> Result, Error> { - self.key_storage.get(key_id).map_err(|e| Error::KeyStorage(e.into())) + self.key_storage.get(key_id) } } @@ -146,7 +146,7 @@ impl ClusterSessionCreator for GenerationSessionCreat fn create(&self, cluster: Arc, master: NodeId, nonce: Option, id: SessionId, _creation_data: Option<()>) -> Result, Error> { // check that there's no finished encryption session with the same id if self.core.key_storage.contains(&id) { - return Err(Error::DuplicateSessionId); + return Err(Error::ServerKeyAlreadyGenerated); } let nonce = self.core.check_session_nonce(&master, nonce)?; @@ -232,6 +232,8 @@ impl ClusterSessionCreator for DecryptionSessi self_node_id: self.core.self_node_id.clone(), master_node_id: master, threshold: encrypted_data.as_ref().map(|ks| ks.threshold).unwrap_or_default(), + configured_nodes_count: cluster.configured_nodes_count(), + connected_nodes_count: cluster.connected_nodes_count(), }, access_key: id.access_key, key_share: encrypted_data, @@ -278,6 +280,8 @@ impl ClusterSessionCreator for SchnorrSign self_node_id: self.core.self_node_id.clone(), master_node_id: master, threshold: encrypted_data.as_ref().map(|ks| ks.threshold).unwrap_or_default(), + configured_nodes_count: cluster.configured_nodes_count(), + connected_nodes_count: cluster.connected_nodes_count(), }, access_key: id.access_key, key_share: encrypted_data, @@ -324,6 +328,8 @@ impl ClusterSessionCreator for EcdsaSigningS self_node_id: self.core.self_node_id.clone(), master_node_id: master, threshold: encrypted_data.as_ref().map(|ks| ks.threshold).unwrap_or_default(), + configured_nodes_count: cluster.configured_nodes_count(), + connected_nodes_count: cluster.connected_nodes_count(), }, access_key: id.access_key, key_share: encrypted_data, @@ -347,18 +353,26 @@ impl ClusterSessionCreator, master: NodeId, nonce: Option, id: SessionIdWithSubSession, _creation_data: Option<()>) -> Result>, Error> { + let configured_nodes_count = cluster.configured_nodes_count(); + let connected_nodes_count = cluster.connected_nodes_count(); let encrypted_data = self.core.read_key_share(&id.id)?; let nonce = self.core.check_session_nonce(&master, nonce)?; - let computer = Arc::new(FastestResultKeyVersionsResultComputer::new(self.core.self_node_id.clone(), encrypted_data.as_ref())); + let computer = Arc::new(FastestResultKeyVersionsResultComputer::new(self.core.self_node_id.clone(), encrypted_data.as_ref(), + configured_nodes_count, configured_nodes_count)); Ok(Arc::new(KeyVersionNegotiationSessionImpl::new(KeyVersionNegotiationSessionParams { meta: ShareChangeSessionMeta { id: id.id.clone(), self_node_id: self.core.self_node_id.clone(), master_node_id: master, + configured_nodes_count: configured_nodes_count, + connected_nodes_count: connected_nodes_count, }, sub_session: id.access_key.clone(), key_share: encrypted_data, @@ -419,6 +433,8 @@ impl ClusterSessionCreator for AdminSess id: id.clone(), self_node_id: self.core.self_node_id.clone(), master_node_id: master, + configured_nodes_count: cluster.configured_nodes_count(), + connected_nodes_count: cluster.connected_nodes_count(), }, transport: ShareAddTransport::new(id.clone(), Some(version), nonce, cluster), key_storage: self.core.key_storage.clone(), @@ -435,6 +451,8 @@ impl ClusterSessionCreator for AdminSess id: id.clone(), self_node_id: self.core.self_node_id.clone(), master_node_id: master, + configured_nodes_count: cluster.configured_nodes_count(), + connected_nodes_count: cluster.connected_nodes_count(), }, cluster: cluster.clone(), key_storage: self.core.key_storage.clone(), diff --git a/secret_store/src/key_server_cluster/connection_trigger.rs b/secret_store/src/key_server_cluster/connection_trigger.rs index e0172b89aca1c86df012896238fb02b5625dff41..2de1a59549782db898fa4f528c220dc11244cba0 100644 --- a/secret_store/src/key_server_cluster/connection_trigger.rs +++ b/secret_store/src/key_server_cluster/connection_trigger.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -23,7 +23,7 @@ use ethkey::Public; use key_server_cluster::{KeyServerSet, KeyServerSetSnapshot}; use key_server_cluster::cluster::{ClusterClient, ClusterConnectionsData}; use key_server_cluster::cluster_sessions::AdminSession; -use types::all::{Error, NodeId}; +use types::{Error, NodeId}; use {NodeKeyPair}; #[derive(Debug, Clone, Copy, PartialEq)] @@ -161,15 +161,20 @@ impl TriggerConnections { fn adjust_connections(self_node_id: &NodeId, data: &mut ClusterConnectionsData, required_set: &BTreeMap) { if !required_set.contains_key(self_node_id) { - trace!(target: "secretstore_net", "{}: isolated from cluser", self_node_id); + if !data.is_isolated { + trace!(target: "secretstore_net", "{}: isolated from cluser", self_node_id); + } + + data.is_isolated = true; data.connections.clear(); data.nodes.clear(); return; } + data.is_isolated = false; for node_to_disconnect in select_nodes_to_disconnect(&data.nodes, required_set) { if let Entry::Occupied(entry) = data.connections.entry(node_to_disconnect.clone()) { - trace!(target: "secretstore_net", "{}: removing connection to {} at {}", + trace!(target: "secretstore_net", "{}: adjusting connections - removing connection to {} at {}", self_node_id, entry.get().node_id(), entry.get().node_address()); entry.remove(); } @@ -204,6 +209,14 @@ mod tests { use super::{Maintain, TriggerConnections, ConnectionsAction, ConnectionTrigger, SimpleConnectionTrigger, select_nodes_to_disconnect, adjust_connections}; + fn default_connection_data() -> ClusterConnectionsData { + ClusterConnectionsData { + is_isolated: false, + nodes: Default::default(), + connections: Default::default(), + } + } + fn create_connections() -> TriggerConnections { TriggerConnections { self_key_pair: Arc::new(PlainNodeKeyPair::new(Random.generate().unwrap())), @@ -252,59 +265,64 @@ mod tests { fn adjust_connections_disconnects_from_all_nodes_if_not_a_part_of_key_server() { let self_node_id = Random.generate().unwrap().public().clone(); let other_node_id = Random.generate().unwrap().public().clone(); - let mut connection_data: ClusterConnectionsData = Default::default(); + let mut connection_data = default_connection_data(); connection_data.nodes.insert(other_node_id.clone(), "127.0.0.1:8081".parse().unwrap()); let required_set = connection_data.nodes.clone(); adjust_connections(&self_node_id, &mut connection_data, &required_set); assert!(connection_data.nodes.is_empty()); + assert!(connection_data.is_isolated); } #[test] fn adjust_connections_connects_to_new_nodes() { let self_node_id = Random.generate().unwrap().public().clone(); let other_node_id = Random.generate().unwrap().public().clone(); - let mut connection_data: ClusterConnectionsData = Default::default(); + let mut connection_data = default_connection_data(); let required_set = vec![(self_node_id.clone(), "127.0.0.1:8081".parse().unwrap()), (other_node_id.clone(), "127.0.0.1:8082".parse().unwrap())].into_iter().collect(); adjust_connections(&self_node_id, &mut connection_data, &required_set); assert!(connection_data.nodes.contains_key(&other_node_id)); + assert!(!connection_data.is_isolated); } #[test] fn adjust_connections_reconnects_from_changed_nodes() { let self_node_id = Random.generate().unwrap().public().clone(); let other_node_id = Random.generate().unwrap().public().clone(); - let mut connection_data: ClusterConnectionsData = Default::default(); + let mut connection_data = default_connection_data(); connection_data.nodes.insert(other_node_id.clone(), "127.0.0.1:8082".parse().unwrap()); let required_set = vec![(self_node_id.clone(), "127.0.0.1:8081".parse().unwrap()), (other_node_id.clone(), "127.0.0.1:8083".parse().unwrap())].into_iter().collect(); adjust_connections(&self_node_id, &mut connection_data, &required_set); assert_eq!(connection_data.nodes.get(&other_node_id), Some(&"127.0.0.1:8083".parse().unwrap())); + assert!(!connection_data.is_isolated); } #[test] fn adjust_connections_disconnects_from_removed_nodes() { let self_node_id = Random.generate().unwrap().public().clone(); let other_node_id = Random.generate().unwrap().public().clone(); - let mut connection_data: ClusterConnectionsData = Default::default(); + let mut connection_data = default_connection_data(); connection_data.nodes.insert(other_node_id.clone(), "127.0.0.1:8082".parse().unwrap()); let required_set = vec![(self_node_id.clone(), "127.0.0.1:8081".parse().unwrap())].into_iter().collect(); adjust_connections(&self_node_id, &mut connection_data, &required_set); assert!(connection_data.nodes.is_empty()); + assert!(!connection_data.is_isolated); } #[test] fn adjust_connections_does_not_connects_to_self() { let self_node_id = Random.generate().unwrap().public().clone(); - let mut connection_data: ClusterConnectionsData = Default::default(); + let mut connection_data = default_connection_data(); let required_set = vec![(self_node_id.clone(), "127.0.0.1:8081".parse().unwrap())].into_iter().collect(); adjust_connections(&self_node_id, &mut connection_data, &required_set); assert!(connection_data.nodes.is_empty()); + assert!(!connection_data.is_isolated); } #[test] @@ -315,7 +333,7 @@ mod tests { let migration_node_id = Random.generate().unwrap().public().clone(); let new_node_id = Random.generate().unwrap().public().clone(); - let mut connections_data: ClusterConnectionsData = Default::default(); + let mut connections_data = default_connection_data(); connections.maintain(ConnectionsAction::ConnectToCurrentSet, &mut connections_data, &KeyServerSetSnapshot { current_set: vec![(self_node_id.clone(), "127.0.0.1:8081".parse().unwrap()), (current_node_id.clone(), "127.0.0.1:8082".parse().unwrap())].into_iter().collect(), @@ -337,7 +355,7 @@ mod tests { let migration_node_id = Random.generate().unwrap().public().clone(); let new_node_id = Random.generate().unwrap().public().clone(); - let mut connections_data: ClusterConnectionsData = Default::default(); + let mut connections_data = default_connection_data(); connections.maintain(ConnectionsAction::ConnectToMigrationSet, &mut connections_data, &KeyServerSetSnapshot { current_set: vec![(current_node_id.clone(), "127.0.0.1:8082".parse().unwrap())].into_iter().collect(), new_set: vec![(new_node_id.clone(), "127.0.0.1:8083".parse().unwrap())].into_iter().collect(), @@ -354,7 +372,7 @@ mod tests { #[test] fn simple_connections_trigger_only_maintains_connections() { - let key_server_set = Arc::new(MapKeyServerSet::new(Default::default())); + let key_server_set = Arc::new(MapKeyServerSet::new(false, Default::default())); let self_key_pair = Arc::new(PlainNodeKeyPair::new(Random.generate().unwrap())); let mut trigger = SimpleConnectionTrigger::new(key_server_set, self_key_pair, None); assert_eq!(trigger.on_maintain(), Some(Maintain::Connections)); diff --git a/secret_store/src/key_server_cluster/connection_trigger_with_migration.rs b/secret_store/src/key_server_cluster/connection_trigger_with_migration.rs index 91679928fb632345c9273e42baf500edf134f08a..cc8db3e6652bf1fd057e71082da75d1b6f2e7d6b 100644 --- a/secret_store/src/key_server_cluster/connection_trigger_with_migration.rs +++ b/secret_store/src/key_server_cluster/connection_trigger_with_migration.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -26,7 +26,7 @@ use key_server_cluster::cluster_sessions::{AdminSession, ClusterSession}; use key_server_cluster::jobs::servers_set_change_access_job::ordered_nodes_hash; use key_server_cluster::connection_trigger::{Maintain, ConnectionsAction, ConnectionTrigger, ServersSetChangeSessionCreatorConnector, TriggerConnections}; -use types::all::{Error, NodeId}; +use types::{Error, NodeId}; use {NodeKeyPair}; /// Key servers set change trigger with automated migration procedure. diff --git a/secret_store/src/key_server_cluster/io/deadline.rs b/secret_store/src/key_server_cluster/io/deadline.rs index 1088f4f33722004ce774e6b649a9509e7046dbc9..94a189522799f30a5926effefe8551eabe912b33 100644 --- a/secret_store/src/key_server_cluster/io/deadline.rs +++ b/secret_store/src/key_server_cluster/io/deadline.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/secret_store/src/key_server_cluster/io/handshake.rs b/secret_store/src/key_server_cluster/io/handshake.rs index c48ed27daedd5b99891f75ba80f69a958c529fbf..5081004d0b3118075e31cd1a066227213018e4f2 100644 --- a/secret_store/src/key_server_cluster/io/handshake.rs +++ b/secret_store/src/key_server_cluster/io/handshake.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -37,7 +37,7 @@ use std::sync::Arc; use std::collections::BTreeSet; use futures::{Future, Poll, Async}; use tokio_io::{AsyncRead, AsyncWrite}; -use ethcrypto::ecdh::agree; +use ethkey::crypto::ecdh::agree; use ethkey::{Random, Generator, KeyPair, Public, Signature, verify_public, sign, recover}; use ethereum_types::H256; use key_server_cluster::{NodeId, Error, NodeKeyPair}; diff --git a/secret_store/src/key_server_cluster/io/message.rs b/secret_store/src/key_server_cluster/io/message.rs index 94030ef416df252009df1f1ada64b79917075e39..e8e01a91f52a16a83b217d4ff2e601600a521a6e 100644 --- a/secret_store/src/key_server_cluster/io/message.rs +++ b/secret_store/src/key_server_cluster/io/message.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -19,7 +19,7 @@ use std::u16; use std::ops::Deref; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use serde_json; -use ethcrypto::ecies; +use ethkey::crypto::ecies; use ethkey::{Secret, KeyPair}; use ethkey::math::curve_order; use ethereum_types::{H256, U256}; @@ -306,7 +306,7 @@ pub mod tests { use futures::Poll; use tokio_io::{AsyncRead, AsyncWrite}; use ethkey::{Random, Generator, KeyPair}; - use ethcrypto::ecdh::agree; + use ethkey::crypto::ecdh::agree; use key_server_cluster::Error; use key_server_cluster::message::Message; use super::{MESSAGE_HEADER_SIZE, CURRENT_HEADER_VERSION, MessageHeader, fix_shared_key, encrypt_message, diff --git a/secret_store/src/key_server_cluster/io/mod.rs b/secret_store/src/key_server_cluster/io/mod.rs index dfea336830fd487e2781047a0da0d0fdbf73abaf..02adb72ad77f152a62d5e122e81c0beb6b39419b 100644 --- a/secret_store/src/key_server_cluster/io/mod.rs +++ b/secret_store/src/key_server_cluster/io/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/secret_store/src/key_server_cluster/io/read_header.rs b/secret_store/src/key_server_cluster/io/read_header.rs index 2fd8960e30fdf7be9c4cfda9c6cfb025c1a814a6..803e01b95b4ae972f9896b7988b05633c3cd749c 100644 --- a/secret_store/src/key_server_cluster/io/read_header.rs +++ b/secret_store/src/key_server_cluster/io/read_header.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/secret_store/src/key_server_cluster/io/read_message.rs b/secret_store/src/key_server_cluster/io/read_message.rs index 1ffb98792a50d7e88c276226e19e157c3d0cabc7..b1d0395d57acce4e9d34039e6b86e3663134c2ba 100644 --- a/secret_store/src/key_server_cluster/io/read_message.rs +++ b/secret_store/src/key_server_cluster/io/read_message.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/secret_store/src/key_server_cluster/io/read_payload.rs b/secret_store/src/key_server_cluster/io/read_payload.rs index 1246092e90a1db0b6090eb24ed3b56c503a8e5e8..da4f4d3c019997f11850edea0c51d459545f2a27 100644 --- a/secret_store/src/key_server_cluster/io/read_payload.rs +++ b/secret_store/src/key_server_cluster/io/read_payload.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/secret_store/src/key_server_cluster/io/shared_tcp_stream.rs b/secret_store/src/key_server_cluster/io/shared_tcp_stream.rs index a847b14280ce7c8a692d75d02fdf4e7606359041..64afbbe82f37b29918af1e9e1beb4b5413b6d71b 100644 --- a/secret_store/src/key_server_cluster/io/shared_tcp_stream.rs +++ b/secret_store/src/key_server_cluster/io/shared_tcp_stream.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/secret_store/src/key_server_cluster/io/write_message.rs b/secret_store/src/key_server_cluster/io/write_message.rs index 8a89cf4552a4e2fa547cc3164e6fca894bff3530..d337a3705a01a76376f35ef5a5b06379da8eb86d 100644 --- a/secret_store/src/key_server_cluster/io/write_message.rs +++ b/secret_store/src/key_server_cluster/io/write_message.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/secret_store/src/key_server_cluster/jobs/consensus_session.rs b/secret_store/src/key_server_cluster/jobs/consensus_session.rs index 42de71b544d4379c3a3e1e4d57299a32bf810faf..6d2866750ee1d5e03ec2400ae91612bbc06dcb0a 100644 --- a/secret_store/src/key_server_cluster/jobs/consensus_session.rs +++ b/secret_store/src/key_server_cluster/jobs/consensus_session.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -17,7 +17,7 @@ use std::collections::BTreeSet; use key_server_cluster::{Error, NodeId, SessionMeta, Requester}; use key_server_cluster::message::ConsensusMessage; -use key_server_cluster::jobs::job_session::{JobSession, JobSessionState, JobTransport, JobExecutor}; +use key_server_cluster::jobs::job_session::{JobSession, JobSessionState, JobTransport, JobExecutor, JobPartialRequestAction}; /// Consensus session state. #[derive(Debug, Clone, Copy, PartialEq)] @@ -114,7 +114,6 @@ impl &JobSession { self.computation_job.as_ref() .expect("computation_job must only be called on master nodes") @@ -140,15 +139,15 @@ impl) -> Result<(), Error> { debug_assert!(self.meta.self_node_id == self.meta.master_node_id); - let initialization_result = self.consensus_job.initialize(nodes, false); + let initialization_result = self.consensus_job.initialize(nodes, None, false); self.state = ConsensusSessionState::EstablishingConsensus; - self.process_result(initialization_result) + self.process_result(initialization_result.map(|_| ())) } /// Process consensus request message. pub fn on_consensus_partial_request(&mut self, sender: &NodeId, request: ConsensusExecutor::PartialJobRequest) -> Result<(), Error> { let consensus_result = self.consensus_job.on_partial_request(sender, request); - self.process_result(consensus_result) + self.process_result(consensus_result.map(|_| ())) } /// Process consensus message response. @@ -179,19 +178,22 @@ impl Result<(), Error> { + pub fn disseminate_jobs(&mut self, executor: ComputationExecutor, transport: ComputationTransport, broadcast_self_response: bool) -> Result, Error> { let consensus_group = self.select_consensus_group()?.clone(); self.consensus_group.clear(); let mut computation_job = JobSession::new(self.meta.clone(), executor, transport); - let computation_result = computation_job.initialize(consensus_group, broadcast_self_response); + let computation_result = computation_job.initialize(consensus_group, None, broadcast_self_response); self.computation_job = Some(computation_job); self.state = ConsensusSessionState::WaitingForPartialResults; - self.process_result(computation_result) + match computation_result { + Ok(computation_result) => self.process_result(Ok(())).map(|_| computation_result), + Err(error) => Err(self.process_result(Err(error)).unwrap_err()), + } } /// Process job request on slave node. - pub fn on_job_request(&mut self, node: &NodeId, request: ComputationExecutor::PartialJobRequest, executor: ComputationExecutor, transport: ComputationTransport) -> Result<(), Error> { + pub fn on_job_request(&mut self, node: &NodeId, request: ComputationExecutor::PartialJobRequest, executor: ComputationExecutor, transport: ComputationTransport) -> Result, Error> { if &self.meta.master_node_id != node { return Err(Error::InvalidMessage); } @@ -230,38 +232,42 @@ impl Result { + pub fn on_node_error(&mut self, node: &NodeId, error: Error) -> Result { let is_self_master = self.meta.master_node_id == self.meta.self_node_id; let is_node_master = self.meta.master_node_id == *node; let (is_restart_needed, timeout_result) = match self.state { ConsensusSessionState::WaitingForInitialization if is_self_master => { // it is strange to receive error before session is initialized && slave doesn't know access_key - // => fatal error + // => unreachable self.state = ConsensusSessionState::Failed; (false, Err(Error::ConsensusUnreachable)) } ConsensusSessionState::WaitingForInitialization if is_node_master => { - // can not establish consensus - // => fatal error + // error from master node before establishing consensus + // => unreachable self.state = ConsensusSessionState::Failed; - (false, Err(Error::ConsensusUnreachable)) + (false, Err(if !error.is_non_fatal() { + Error::ConsensusUnreachable + } else { + Error::ConsensusTemporaryUnreachable + })) }, ConsensusSessionState::EstablishingConsensus => { debug_assert!(is_self_master); // consensus still can be established // => try to live without this node - (false, self.consensus_job.on_node_error(node)) + (false, self.consensus_job.on_node_error(node, error)) }, ConsensusSessionState::ConsensusEstablished => { // we could try to continue without this node, if enough nodes left - (false, self.consensus_job.on_node_error(node)) + (false, self.consensus_job.on_node_error(node, error)) }, ConsensusSessionState::WaitingForPartialResults => { // check if *current* computation job can continue without this node let is_computation_node = self.computation_job.as_mut() .expect("WaitingForPartialResults state is only set when computation_job is created; qed") - .on_node_error(node) + .on_node_error(node, error.clone()) .is_err(); if !is_computation_node { // it is not used by current computation job @@ -273,7 +279,7 @@ impl return Ok(false), @@ -310,7 +316,7 @@ impl - self.consensus_job.on_partial_request(sender, message.requester.clone().into()), + self.consensus_job.on_partial_request(sender, message.requester.clone().into()).map(|_| ()), &ConsensusMessage::ConfirmConsensusInitialization(ref message) => self.consensus_job.on_partial_response(sender, message.is_confirmed), }; @@ -361,7 +367,7 @@ impl Result, Error> { - let key_version = self.key_share.version(&self.key_version).map_err(|e| Error::KeyStorage(e.into()))?; + let key_version = self.key_share.version(&self.key_version)?; if partial_request.other_nodes_ids.len() != self.key_share.threshold || partial_request.other_nodes_ids.contains(&self.self_node_id) || partial_request.other_nodes_ids.iter().any(|n| !key_version.id_numbers.contains_key(n)) { @@ -143,10 +144,11 @@ impl JobExecutor for DecryptionJob { let decrypt_shadow = if partial_request.is_shadow_decryption { Some(math::generate_random_scalar()?) } else { None }; let common_point = self.key_share.common_point.as_ref().expect("DecryptionJob is only created when common_point is known; qed"); let (shadow_point, decrypt_shadow) = math::compute_node_shadow_point(&self.access_key, &common_point, &node_shadow, decrypt_shadow)?; + Ok(JobPartialRequestAction::Respond(PartialDecryptionResponse { request_id: partial_request.id, shadow_point: shadow_point, - decrypt_shadow: match decrypt_shadow { + decrypt_shadow: match decrypt_shadow.clone() { None => None, Some(decrypt_shadow) => Some(encrypt(&self.requester, &DEFAULT_MAC, &**decrypt_shadow)?), }, diff --git a/secret_store/src/key_server_cluster/jobs/dummy_job.rs b/secret_store/src/key_server_cluster/jobs/dummy_job.rs index 3e84c0d49d2f9f3bb5464a8188a4f6d09f74ae48..f7e771d1558ea7ad8f5bfa58f0fbe9d0a4aee608 100644 --- a/secret_store/src/key_server_cluster/jobs/dummy_job.rs +++ b/secret_store/src/key_server_cluster/jobs/dummy_job.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/secret_store/src/key_server_cluster/jobs/job_session.rs b/secret_store/src/key_server_cluster/jobs/job_session.rs index 0299fdb141d6bb554584233de4dd3e2f4a211101..ab0300db3618613e36b1ff9af8e7d2db3b3130fb 100644 --- a/secret_store/src/key_server_cluster/jobs/job_session.rs +++ b/secret_store/src/key_server_cluster/jobs/job_session.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -101,8 +101,8 @@ struct JobSessionData { struct ActiveJobSessionData { /// Active partial requests. requests: BTreeSet, - /// Rejects to partial requests. - rejects: BTreeSet, + /// Rejects to partial requests (maps to true, if reject is fatal). + rejects: BTreeMap, /// Received partial responses. responses: BTreeMap, } @@ -149,7 +149,7 @@ impl JobSession where Executor: JobExe /// Get rejects. #[cfg(test)] - pub fn rejects(&self) -> &BTreeSet { + pub fn rejects(&self) -> &BTreeMap { debug_assert!(self.meta.self_node_id == self.meta.master_node_id); &self.data.active_data.as_ref() @@ -197,11 +197,15 @@ impl JobSession where Executor: JobExe } /// Initialize. - pub fn initialize(&mut self, nodes: BTreeSet, broadcast_self_response: bool) -> Result<(), Error> { + pub fn initialize(&mut self, nodes: BTreeSet, self_response: Option, broadcast_self_response: bool) -> Result, Error> { debug_assert!(self.meta.self_node_id == self.meta.master_node_id); if nodes.len() < self.meta.threshold + 1 { - return Err(Error::ConsensusUnreachable); + return Err(if self.meta.configured_nodes_count < self.meta.threshold + 1 { + Error::ConsensusUnreachable + } else { + Error::ConsensusTemporaryUnreachable + }); } if self.data.state != JobSessionState::Inactive { @@ -211,19 +215,17 @@ impl JobSession where Executor: JobExe // result from self let active_data = ActiveJobSessionData { requests: nodes.clone(), - rejects: BTreeSet::new(), + rejects: BTreeMap::new(), responses: BTreeMap::new(), }; let waits_for_self = active_data.requests.contains(&self.meta.self_node_id); - let self_response = if waits_for_self { - let partial_request = self.executor.prepare_partial_request(&self.meta.self_node_id, &active_data.requests)?; - Some(self.executor.process_partial_request(partial_request)?) - } else { - None - }; let self_response = match self_response { - Some(JobPartialRequestAction::Respond(self_response)) => Some(self_response), - Some(JobPartialRequestAction::Reject(self_response)) => Some(self_response), + Some(self_response) => Some(self_response), + None if waits_for_self => { + let partial_request = self.executor.prepare_partial_request(&self.meta.self_node_id, &active_data.requests)?; + let self_response = self.executor.process_partial_request(partial_request)?; + Some(self_response.take_response()) + }, None => None, }; @@ -249,11 +251,11 @@ impl JobSession where Executor: JobExe } } - Ok(()) + Ok(self_response) } /// When partial request is received by slave node. - pub fn on_partial_request(&mut self, node: &NodeId, request: Executor::PartialJobRequest) -> Result<(), Error> { + pub fn on_partial_request(&mut self, node: &NodeId, request: Executor::PartialJobRequest) -> Result, Error> { if node != &self.meta.master_node_id { return Err(Error::InvalidMessage); } @@ -264,17 +266,19 @@ impl JobSession where Executor: JobExe return Err(Error::InvalidStateForRequest); } - let partial_response = match self.executor.process_partial_request(request)? { - JobPartialRequestAction::Respond(partial_response) => { + let partial_request_action = self.executor.process_partial_request(request)?; + let partial_response = match partial_request_action { + JobPartialRequestAction::Respond(ref partial_response) => { self.data.state = JobSessionState::Finished; - partial_response + partial_response.clone() }, - JobPartialRequestAction::Reject(partial_response) => { + JobPartialRequestAction::Reject(ref partial_response) => { self.data.state = JobSessionState::Failed; - partial_response + partial_response.clone() }, }; - self.transport.send_partial_response(node, partial_response) + self.transport.send_partial_response(node, partial_response)?; + Ok(partial_request_action) } /// When partial request is received by master node. @@ -291,17 +295,18 @@ impl JobSession where Executor: JobExe if !active_data.requests.remove(node) { return Err(Error::InvalidNodeForRequest); } - + match self.executor.check_partial_response(node, &response)? { JobPartialResponseAction::Ignore => Ok(()), JobPartialResponseAction::Reject => { - active_data.rejects.insert(node.clone()); + // direct reject is always considered as fatal + active_data.rejects.insert(node.clone(), true); if active_data.requests.len() + active_data.responses.len() >= self.meta.threshold + 1 { return Ok(()); } self.data.state = JobSessionState::Failed; - Err(Error::ConsensusUnreachable) + Err(consensus_unreachable(&active_data.rejects)) }, JobPartialResponseAction::Accept => { active_data.responses.insert(node.clone(), response); @@ -316,22 +321,26 @@ impl JobSession where Executor: JobExe } /// When error from node is received. - pub fn on_node_error(&mut self, node: &NodeId) -> Result<(), Error> { + pub fn on_node_error(&mut self, node: &NodeId, error: Error) -> Result<(), Error> { if self.meta.self_node_id != self.meta.master_node_id { if node != &self.meta.master_node_id { return Ok(()); } self.data.state = JobSessionState::Failed; - return Err(Error::ConsensusUnreachable); + return Err(if !error.is_non_fatal() { + Error::ConsensusUnreachable + } else { + Error::ConsensusTemporaryUnreachable + }); } if let Some(active_data) = self.data.active_data.as_mut() { - if active_data.rejects.contains(node) { + if active_data.rejects.contains_key(node) { return Ok(()); } if active_data.requests.remove(node) || active_data.responses.remove(node).is_some() { - active_data.rejects.insert(node.clone()); + active_data.rejects.insert(node.clone(), !error.is_non_fatal()); if self.data.state == JobSessionState::Finished && active_data.responses.len() < self.meta.threshold + 1 { self.data.state = JobSessionState::Active; } @@ -340,7 +349,7 @@ impl JobSession where Executor: JobExe } self.data.state = JobSessionState::Failed; - return Err(Error::ConsensusUnreachable); + return Err(consensus_unreachable(&active_data.rejects)); } } @@ -354,10 +363,30 @@ impl JobSession where Executor: JobExe } self.data.state = JobSessionState::Failed; - Err(Error::ConsensusUnreachable) + // we have started session => consensus is possible in theory, but now it has failed with timeout + Err(Error::ConsensusTemporaryUnreachable) } } +impl JobPartialRequestAction { + /// Take actual response. + pub fn take_response(self) -> PartialJobResponse { + match self { + JobPartialRequestAction::Respond(response) => response, + JobPartialRequestAction::Reject(response) => response, + } + } +} + +/// Returns appropriate 'consensus unreachable' error. +fn consensus_unreachable(rejects: &BTreeMap) -> Error { + // when >= 50% of nodes have responded with fatal reject => ConsensusUnreachable + if rejects.values().filter(|r| **r).count() >= rejects.len() / 2 { + Error::ConsensusUnreachable + } else { + Error::ConsensusTemporaryUnreachable + } +} #[cfg(test)] pub mod tests { @@ -405,24 +434,40 @@ pub mod tests { } pub fn make_master_session_meta(threshold: usize) -> SessionMeta { - SessionMeta { id: SessionId::default(), master_node_id: NodeId::from(1), self_node_id: NodeId::from(1), threshold: threshold } + SessionMeta { id: SessionId::default(), master_node_id: NodeId::from(1), self_node_id: NodeId::from(1), threshold: threshold, + configured_nodes_count: 5, connected_nodes_count: 5 } } pub fn make_slave_session_meta(threshold: usize) -> SessionMeta { - SessionMeta { id: SessionId::default(), master_node_id: NodeId::from(1), self_node_id: NodeId::from(2), threshold: threshold } + SessionMeta { id: SessionId::default(), master_node_id: NodeId::from(1), self_node_id: NodeId::from(2), threshold: threshold, + configured_nodes_count: 5, connected_nodes_count: 5 } + } + + #[test] + fn job_initialize_fails_if_not_enough_nodes_for_threshold_total() { + let mut job = JobSession::new(make_master_session_meta(1), SquaredSumJobExecutor, DummyJobTransport::default()); + job.meta.configured_nodes_count = 1; + assert_eq!(job.initialize(vec![Public::from(1)].into_iter().collect(), None, false).unwrap_err(), Error::ConsensusUnreachable); + } + + #[test] + fn job_initialize_fails_if_not_enough_nodes_for_threshold_connected() { + let mut job = JobSession::new(make_master_session_meta(1), SquaredSumJobExecutor, DummyJobTransport::default()); + job.meta.connected_nodes_count = 3; + assert_eq!(job.initialize(vec![Public::from(1)].into_iter().collect(), None, false).unwrap_err(), Error::ConsensusTemporaryUnreachable); } #[test] fn job_initialize_fails_if_not_inactive() { let mut job = JobSession::new(make_master_session_meta(0), SquaredSumJobExecutor, DummyJobTransport::default()); - job.initialize(vec![Public::from(1)].into_iter().collect(), false).unwrap(); - assert_eq!(job.initialize(vec![Public::from(1)].into_iter().collect(), false).unwrap_err(), Error::InvalidStateForRequest); + job.initialize(vec![Public::from(1)].into_iter().collect(), None, false).unwrap(); + assert_eq!(job.initialize(vec![Public::from(1)].into_iter().collect(), None, false).unwrap_err(), Error::InvalidStateForRequest); } #[test] fn job_initialization_leads_to_finish_if_single_node_is_required() { let mut job = JobSession::new(make_master_session_meta(0), SquaredSumJobExecutor, DummyJobTransport::default()); - job.initialize(vec![Public::from(1)].into_iter().collect(), false).unwrap(); + job.initialize(vec![Public::from(1)].into_iter().collect(), None, false).unwrap(); assert_eq!(job.state(), JobSessionState::Finished); assert!(job.is_result_ready()); assert_eq!(job.result(), Ok(4)); @@ -431,7 +476,7 @@ pub mod tests { #[test] fn job_initialization_does_not_leads_to_finish_if_single_other_node_is_required() { let mut job = JobSession::new(make_master_session_meta(0), SquaredSumJobExecutor, DummyJobTransport::default()); - job.initialize(vec![Public::from(2)].into_iter().collect(), false).unwrap(); + job.initialize(vec![Public::from(2)].into_iter().collect(), None, false).unwrap(); assert_eq!(job.state(), JobSessionState::Active); } @@ -474,7 +519,7 @@ pub mod tests { #[test] fn job_response_fails_if_comes_to_failed_state() { let mut job = JobSession::new(make_master_session_meta(0), SquaredSumJobExecutor, DummyJobTransport::default()); - job.initialize(vec![Public::from(2)].into_iter().collect(), false).unwrap(); + job.initialize(vec![Public::from(2)].into_iter().collect(), None, false).unwrap(); job.on_session_timeout().unwrap_err(); assert_eq!(job.on_partial_response(&NodeId::from(2), 2).unwrap_err(), Error::InvalidStateForRequest); } @@ -482,14 +527,14 @@ pub mod tests { #[test] fn job_response_fails_if_comes_from_unknown_node() { let mut job = JobSession::new(make_master_session_meta(0), SquaredSumJobExecutor, DummyJobTransport::default()); - job.initialize(vec![Public::from(2)].into_iter().collect(), false).unwrap(); + job.initialize(vec![Public::from(2)].into_iter().collect(), None, false).unwrap(); assert_eq!(job.on_partial_response(&NodeId::from(3), 2).unwrap_err(), Error::InvalidNodeForRequest); } #[test] fn job_response_leads_to_failure_if_too_few_nodes_left() { let mut job = JobSession::new(make_master_session_meta(1), SquaredSumJobExecutor, DummyJobTransport::default()); - job.initialize(vec![Public::from(1), Public::from(2)].into_iter().collect(), false).unwrap(); + job.initialize(vec![Public::from(1), Public::from(2)].into_iter().collect(), None, false).unwrap(); assert_eq!(job.state(), JobSessionState::Active); assert_eq!(job.on_partial_response(&NodeId::from(2), 3).unwrap_err(), Error::ConsensusUnreachable); assert_eq!(job.state(), JobSessionState::Failed); @@ -498,7 +543,7 @@ pub mod tests { #[test] fn job_response_succeeds() { let mut job = JobSession::new(make_master_session_meta(2), SquaredSumJobExecutor, DummyJobTransport::default()); - job.initialize(vec![Public::from(1), Public::from(2), Public::from(3)].into_iter().collect(), false).unwrap(); + job.initialize(vec![Public::from(1), Public::from(2), Public::from(3)].into_iter().collect(), None, false).unwrap(); assert_eq!(job.state(), JobSessionState::Active); assert!(!job.is_result_ready()); job.on_partial_response(&NodeId::from(2), 2).unwrap(); @@ -509,7 +554,7 @@ pub mod tests { #[test] fn job_response_leads_to_finish() { let mut job = JobSession::new(make_master_session_meta(1), SquaredSumJobExecutor, DummyJobTransport::default()); - job.initialize(vec![Public::from(1), Public::from(2)].into_iter().collect(), false).unwrap(); + job.initialize(vec![Public::from(1), Public::from(2)].into_iter().collect(), None, false).unwrap(); assert_eq!(job.state(), JobSessionState::Active); job.on_partial_response(&NodeId::from(2), 2).unwrap(); assert_eq!(job.state(), JobSessionState::Finished); @@ -519,7 +564,7 @@ pub mod tests { fn job_node_error_ignored_when_slave_disconnects_from_slave() { let mut job = JobSession::new(make_slave_session_meta(1), SquaredSumJobExecutor, DummyJobTransport::default()); assert_eq!(job.state(), JobSessionState::Inactive); - job.on_node_error(&NodeId::from(3)).unwrap(); + job.on_node_error(&NodeId::from(3), Error::AccessDenied).unwrap(); assert_eq!(job.state(), JobSessionState::Inactive); } @@ -527,51 +572,51 @@ pub mod tests { fn job_node_error_leads_to_fail_when_slave_disconnects_from_master() { let mut job = JobSession::new(make_slave_session_meta(1), SquaredSumJobExecutor, DummyJobTransport::default()); assert_eq!(job.state(), JobSessionState::Inactive); - assert_eq!(job.on_node_error(&NodeId::from(1)).unwrap_err(), Error::ConsensusUnreachable); + assert_eq!(job.on_node_error(&NodeId::from(1), Error::AccessDenied).unwrap_err(), Error::ConsensusUnreachable); assert_eq!(job.state(), JobSessionState::Failed); } #[test] fn job_node_error_ignored_when_disconnects_from_rejected() { let mut job = JobSession::new(make_master_session_meta(1), SquaredSumJobExecutor, DummyJobTransport::default()); - job.initialize(vec![Public::from(1), Public::from(2), Public::from(3)].into_iter().collect(), false).unwrap(); + job.initialize(vec![Public::from(1), Public::from(2), Public::from(3)].into_iter().collect(), None, false).unwrap(); assert_eq!(job.state(), JobSessionState::Active); job.on_partial_response(&NodeId::from(2), 3).unwrap(); - job.on_node_error(&NodeId::from(2)).unwrap(); + job.on_node_error(&NodeId::from(2), Error::AccessDenied).unwrap(); assert_eq!(job.state(), JobSessionState::Active); } #[test] fn job_node_error_ignored_when_disconnects_from_unknown() { let mut job = JobSession::new(make_master_session_meta(1), SquaredSumJobExecutor, DummyJobTransport::default()); - job.initialize(vec![Public::from(1), Public::from(2)].into_iter().collect(), false).unwrap(); + job.initialize(vec![Public::from(1), Public::from(2)].into_iter().collect(), None, false).unwrap(); assert_eq!(job.state(), JobSessionState::Active); - job.on_node_error(&NodeId::from(3)).unwrap(); + job.on_node_error(&NodeId::from(3), Error::AccessDenied).unwrap(); assert_eq!(job.state(), JobSessionState::Active); } #[test] fn job_node_error_ignored_when_disconnects_from_requested_and_enough_nodes_left() { let mut job = JobSession::new(make_master_session_meta(1), SquaredSumJobExecutor, DummyJobTransport::default()); - job.initialize(vec![Public::from(1), Public::from(2), Public::from(3)].into_iter().collect(), false).unwrap(); + job.initialize(vec![Public::from(1), Public::from(2), Public::from(3)].into_iter().collect(), None, false).unwrap(); assert_eq!(job.state(), JobSessionState::Active); - job.on_node_error(&NodeId::from(3)).unwrap(); + job.on_node_error(&NodeId::from(3), Error::AccessDenied).unwrap(); assert_eq!(job.state(), JobSessionState::Active); } #[test] fn job_node_error_leads_to_fail_when_disconnects_from_requested_and_not_enough_nodes_left() { let mut job = JobSession::new(make_master_session_meta(1), SquaredSumJobExecutor, DummyJobTransport::default()); - job.initialize(vec![Public::from(1), Public::from(2)].into_iter().collect(), false).unwrap(); + job.initialize(vec![Public::from(1), Public::from(2)].into_iter().collect(), None, false).unwrap(); assert_eq!(job.state(), JobSessionState::Active); - assert_eq!(job.on_node_error(&NodeId::from(2)).unwrap_err(), Error::ConsensusUnreachable); + assert_eq!(job.on_node_error(&NodeId::from(2), Error::AccessDenied).unwrap_err(), Error::ConsensusUnreachable); assert_eq!(job.state(), JobSessionState::Failed); } #[test] fn job_broadcasts_self_response() { let mut job = JobSession::new(make_master_session_meta(1), SquaredSumJobExecutor, DummyJobTransport::default()); - job.initialize(vec![Public::from(1), Public::from(2)].into_iter().collect(), true).unwrap(); + job.initialize(vec![Public::from(1), Public::from(2)].into_iter().collect(), None, true).unwrap(); assert_eq!(job.state(), JobSessionState::Active); assert_eq!(job.transport().response(), (NodeId::from(2), 4)); } @@ -579,8 +624,38 @@ pub mod tests { #[test] fn job_does_not_broadcasts_self_response() { let mut job = JobSession::new(make_master_session_meta(1), SquaredSumJobExecutor, DummyJobTransport::default()); - job.initialize(vec![Public::from(1), Public::from(2)].into_iter().collect(), false).unwrap(); + job.initialize(vec![Public::from(1), Public::from(2)].into_iter().collect(), None, false).unwrap(); assert_eq!(job.state(), JobSessionState::Active); assert!(job.transport().is_empty_response()); } + + #[test] + fn job_fails_with_temp_error_if_more_than_half_nodes_respond_with_temp_error() { + let mut job = JobSession::new(make_master_session_meta(2), SquaredSumJobExecutor, DummyJobTransport::default()); + job.initialize(vec![Public::from(1), Public::from(2), Public::from(3), Public::from(4)].into_iter().collect(), None, false).unwrap(); + job.on_node_error(&NodeId::from(2), Error::NodeDisconnected).unwrap(); + assert_eq!(job.on_node_error(&NodeId::from(3), Error::NodeDisconnected).unwrap_err(), Error::ConsensusTemporaryUnreachable); + } + + #[test] + fn job_fails_with_temp_error_if_more_than_half_rejects_are_temp() { + let mut job = JobSession::new(make_master_session_meta(2), SquaredSumJobExecutor, DummyJobTransport::default()); + job.initialize(vec![Public::from(1), Public::from(2), Public::from(3), Public::from(4)].into_iter().collect(), None, false).unwrap(); + job.on_node_error(&NodeId::from(2), Error::NodeDisconnected).unwrap(); + assert_eq!(job.on_node_error(&NodeId::from(3), Error::NodeDisconnected).unwrap_err(), Error::ConsensusTemporaryUnreachable); + } + + #[test] + fn job_fails_if_more_than_half_rejects_are_non_temp() { + let mut job = JobSession::new(make_master_session_meta(2), SquaredSumJobExecutor, DummyJobTransport::default()); + job.initialize(vec![Public::from(1), Public::from(2), Public::from(3), Public::from(4)].into_iter().collect(), None, false).unwrap(); + job.on_node_error(&NodeId::from(2), Error::AccessDenied).unwrap(); + assert_eq!(job.on_node_error(&NodeId::from(3), Error::AccessDenied).unwrap_err(), Error::ConsensusUnreachable); + } + + #[test] + fn job_fails_with_temp_error_when_temp_error_is_reported_by_master_node() { + let mut job = JobSession::new(make_slave_session_meta(2), SquaredSumJobExecutor, DummyJobTransport::default()); + assert_eq!(job.on_node_error(&NodeId::from(1), Error::NodeDisconnected).unwrap_err(), Error::ConsensusTemporaryUnreachable); + } } diff --git a/secret_store/src/key_server_cluster/jobs/key_access_job.rs b/secret_store/src/key_server_cluster/jobs/key_access_job.rs index 2cb686e6c2104fa603411b3579a81cce3cb09456..7ded90afabb3b10b255a43c8f50aa9e6f435d1d0 100644 --- a/secret_store/src/key_server_cluster/jobs/key_access_job.rs +++ b/secret_store/src/key_server_cluster/jobs/key_access_job.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -78,8 +78,7 @@ impl JobExecutor for KeyAccessJob { } self.requester = Some(partial_request.clone()); - self.acl_storage.check(&partial_request.public(&self.id).ok_or(Error::InsufficientRequesterData)?, &self.id) - .map_err(|_| Error::AccessDenied) + self.acl_storage.check(partial_request.address(&self.id).map_err(Error::InsufficientRequesterData)?, &self.id) .map(|is_confirmed| if is_confirmed { JobPartialRequestAction::Respond(true) } else { JobPartialRequestAction::Reject(false) }) } diff --git a/secret_store/src/key_server_cluster/jobs/mod.rs b/secret_store/src/key_server_cluster/jobs/mod.rs index 817f09b71dde2bd413d79f3cc8a768ecd04e6fd0..75d07e313b790c6198ef489bcc60515fbd5f43d4 100644 --- a/secret_store/src/key_server_cluster/jobs/mod.rs +++ b/secret_store/src/key_server_cluster/jobs/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/secret_store/src/key_server_cluster/jobs/servers_set_change_access_job.rs b/secret_store/src/key_server_cluster/jobs/servers_set_change_access_job.rs index 1d1628692695130fa63567c2096645e5b8065669..6c142d2a2f84f7e3e2627d48607113b0d371d99a 100644 --- a/secret_store/src/key_server_cluster/jobs/servers_set_change_access_job.rs +++ b/secret_store/src/key_server_cluster/jobs/servers_set_change_access_job.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/secret_store/src/key_server_cluster/jobs/signing_job_ecdsa.rs b/secret_store/src/key_server_cluster/jobs/signing_job_ecdsa.rs index 561a8e0f87ca5dce8183e5a7634876b2cfa0b7af..6349c2e7db493a238c9d12bccc3518331e2bb4db 100644 --- a/secret_store/src/key_server_cluster/jobs/signing_job_ecdsa.rs +++ b/secret_store/src/key_server_cluster/jobs/signing_job_ecdsa.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -108,7 +108,7 @@ impl JobExecutor for EcdsaSigningJob { fn process_partial_request(&mut self, partial_request: EcdsaPartialSigningRequest) -> Result, Error> { let inversed_nonce_coeff_mul_nonce = math::compute_secret_mul(&partial_request.inversed_nonce_coeff, &self.inv_nonce_share)?; - let key_version = self.key_share.version(&self.key_version).map_err(|e| Error::KeyStorage(e.into()))?; + let key_version = self.key_share.version(&self.key_version)?; let signature_r = math::compute_ecdsa_r(&self.nonce_public)?; let inv_nonce_mul_secret = math::compute_secret_mul(&inversed_nonce_coeff_mul_nonce, &key_version.secret_share)?; let partial_signature_s = math::compute_ecdsa_s_share( @@ -134,7 +134,7 @@ impl JobExecutor for EcdsaSigningJob { } fn compute_response(&self, partial_responses: &BTreeMap) -> Result { - let key_version = self.key_share.version(&self.key_version).map_err(|e| Error::KeyStorage(e.into()))?; + let key_version = self.key_share.version(&self.key_version)?; if partial_responses.keys().any(|n| !key_version.id_numbers.contains_key(n)) { return Err(Error::InvalidMessage); } diff --git a/secret_store/src/key_server_cluster/jobs/signing_job_schnorr.rs b/secret_store/src/key_server_cluster/jobs/signing_job_schnorr.rs index 6246a728de96805b86bc0131b3f61d75071499c4..4d1a0e7d9092e12c289b60db44d2e0b8202ad6fa 100644 --- a/secret_store/src/key_server_cluster/jobs/signing_job_schnorr.rs +++ b/secret_store/src/key_server_cluster/jobs/signing_job_schnorr.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -107,7 +107,7 @@ impl JobExecutor for SchnorrSigningJob { } fn process_partial_request(&mut self, partial_request: SchnorrPartialSigningRequest) -> Result, Error> { - let key_version = self.key_share.version(&self.key_version).map_err(|e| Error::KeyStorage(e.into()))?; + let key_version = self.key_share.version(&self.key_version)?; if partial_request.other_nodes_ids.len() != self.key_share.threshold || partial_request.other_nodes_ids.contains(&self.self_node_id) || partial_request.other_nodes_ids.iter().any(|n| !key_version.id_numbers.contains_key(n)) { @@ -148,4 +148,4 @@ impl JobExecutor for SchnorrSigningJob { Ok((signature_c, signature_s)) } -} \ No newline at end of file +} diff --git a/secret_store/src/key_server_cluster/jobs/unknown_sessions_job.rs b/secret_store/src/key_server_cluster/jobs/unknown_sessions_job.rs index 13f2f8b8bb46ee2ae698154a984a28e6f40d77ce..908afa1ecc6ec1bfd11b66e2bfba15a84d577569 100644 --- a/secret_store/src/key_server_cluster/jobs/unknown_sessions_job.rs +++ b/secret_store/src/key_server_cluster/jobs/unknown_sessions_job.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/secret_store/src/key_server_cluster/math.rs b/secret_store/src/key_server_cluster/math.rs index 78c444822beefb44e0d22e85853392a3b8e01e4d..66f26b50869c0fb243c709238f80e7d20d36b0bd 100644 --- a/secret_store/src/key_server_cluster/math.rs +++ b/secret_store/src/key_server_cluster/math.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -37,7 +37,7 @@ pub fn zero_scalar() -> Secret { pub fn to_scalar(hash: H256) -> Result { let scalar: U256 = hash.into(); let scalar: H256 = (scalar % math::curve_order()).into(); - let scalar = Secret::from_slice(&*scalar); + let scalar = Secret::from(scalar.0); scalar.check_validity()?; Ok(scalar) } @@ -697,7 +697,7 @@ pub mod tests { // === required to generate shares of inv(x) mod r with out revealing // === any information about x or inv(x). // === https://www.researchgate.net/publication/280531698_Robust_Threshold_Elliptic_Curve_Digital_Signature - + // generate shared random secret e for given t let n = artifacts.id_numbers.len(); assert!(t * 2 + 1 <= n); diff --git a/secret_store/src/key_server_cluster/message.rs b/secret_store/src/key_server_cluster/message.rs index 9a93efb03a3909783c318ae7d9a1431bb16a926b..1bc487eb78f2090b943cf1255e353261c493d367 100644 --- a/secret_store/src/key_server_cluster/message.rs +++ b/secret_store/src/key_server_cluster/message.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -18,8 +18,8 @@ use std::fmt; use std::collections::{BTreeSet, BTreeMap}; use ethkey::Secret; use key_server_cluster::SessionId; -use super::{SerializableH256, SerializablePublic, SerializableSecret, SerializableSignature, - SerializableMessageHash, SerializableRequester, SerializableAddress}; +use super::{Error, SerializableH256, SerializablePublic, SerializableSecret, + SerializableSignature, SerializableMessageHash, SerializableRequester, SerializableAddress}; pub type MessageSessionId = SerializableH256; pub type MessageNodeId = SerializablePublic; @@ -272,6 +272,8 @@ pub struct InitializeSession { pub session: MessageSessionId, /// Session-level nonce. pub session_nonce: u64, + /// Session origin address (if any). + pub origin: Option, /// Session author. pub author: SerializableAddress, /// All session participants along with their identification numbers. @@ -344,7 +346,7 @@ pub struct SessionError { /// Session-level nonce. pub session_nonce: u64, /// Error message. - pub error: String, + pub error: Error, } /// When session is completed. @@ -388,7 +390,7 @@ pub struct EncryptionSessionError { /// Session-level nonce. pub session_nonce: u64, /// Error message. - pub error: String, + pub error: Error, } /// Node is asked to be part of consensus group. @@ -427,6 +429,8 @@ pub struct InitializeConsensusSessionWithServersSet { pub struct InitializeConsensusSessionOfShareAdd { /// Key version. pub version: SerializableH256, + /// Nodes that have reported version ownership. + pub version_holders: BTreeSet, /// threshold+1 nodes from old_nodes_set selected for shares redistribution. pub consensus_group: BTreeSet, /// Old nodes set: all non-isolated owners of selected key share version. @@ -507,7 +511,7 @@ pub struct SchnorrSigningSessionError { /// Session-level nonce. pub session_nonce: u64, /// Error message. - pub error: String, + pub error: Error, } /// Schnorr signing session completed. @@ -660,7 +664,7 @@ pub struct EcdsaSigningSessionError { /// Session-level nonce. pub session_nonce: u64, /// Error message. - pub error: String, + pub error: Error, } /// ECDSA signing session completed. @@ -713,6 +717,8 @@ pub struct DecryptionConsensusMessage { pub sub_session: SerializableSecret, /// Session-level nonce. pub session_nonce: u64, + /// Session origin (in consensus initialization message). + pub origin: Option, /// Consensus message. pub message: ConsensusMessage, } @@ -765,7 +771,7 @@ pub struct DecryptionSessionError { /// Session-level nonce. pub session_nonce: u64, /// Error message. - pub error: String, + pub error: Error, } /// When decryption session is completed. @@ -788,6 +794,8 @@ pub struct DecryptionSessionDelegation { pub sub_session: SerializableSecret, /// Session-level nonce. pub session_nonce: u64, + /// Session origin. + pub origin: Option, /// Requester. pub requester: SerializableRequester, /// Key version. @@ -870,6 +878,8 @@ pub struct InitializeShareChangeSession { pub key_id: MessageSessionId, /// Key vesion to use in ShareAdd session. pub version: SerializableH256, + /// Nodes that have confirmed version ownership. + pub version_holders: BTreeSet, /// Master node. pub master_node_id: MessageNodeId, /// Consensus group to use in ShareAdd session. @@ -930,7 +940,7 @@ pub struct ServersSetChangeError { /// Session-level nonce. pub session_nonce: u64, /// Error message. - pub error: String, + pub error: Error, } /// When servers set change session is completed. @@ -993,7 +1003,7 @@ pub struct ShareAddError { /// Session-level nonce. pub session_nonce: u64, /// Error message. - pub error: String, + pub error: Error, } /// Key versions are requested. @@ -1032,7 +1042,17 @@ pub struct KeyVersionsError { /// Session-level nonce. pub session_nonce: u64, /// Error message. - pub error: String, + pub error: Error, + /// Continue action from failed node (if any). This field is oly filled + /// when error has occured when trying to compute result on master node. + pub continue_with: Option, +} + +/// Key version continue action from failed node. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub enum FailedKeyVersionContinueAction { + /// Decryption session: origin + requester. + Decrypt(Option, SerializableAddress), } impl Message { @@ -1053,6 +1073,7 @@ impl Message { _ => false }, Message::KeyVersionNegotiation(KeyVersionNegotiationMessage::RequestKeyVersions(_)) => true, + Message::KeyVersionNegotiation(KeyVersionNegotiationMessage::KeyVersionsError(ref msg)) if msg.continue_with.is_some() => true, Message::ShareAdd(ShareAddMessage::ShareAddConsensusMessage(ref msg)) => match msg.message { ConsensusMessageOfShareAdd::InitializeConsensusSession(_) => true, _ => false diff --git a/secret_store/src/key_server_cluster/mod.rs b/secret_store/src/key_server_cluster/mod.rs index 17334e838ae73db987160c89f3df969e6a408ae7..018d70d305feb04d67a9c7c2518baa8381b65546 100644 --- a/secret_store/src/key_server_cluster/mod.rs +++ b/secret_store/src/key_server_cluster/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -14,14 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use std::fmt; -use std::io::Error as IoError; -use ethkey; -use ethcrypto; -use super::types::all::ServerKeyId; +use super::types::ServerKeyId; pub use super::traits::NodeKeyPair; -pub use super::types::all::{NodeId, Requester, EncryptedDocumentKeyShadow}; +pub use super::types::{Error, NodeId, Requester, EncryptedDocumentKeyShadow}; pub use super::acl_storage::AclStorage; pub use super::key_storage::{KeyStorage, DocumentKeyShare, DocumentKeyShareVersion}; pub use super::key_server_set::{is_migration_required, KeyServerSet, KeyServerSetSnapshot, KeyServerSetMigration}; @@ -53,126 +49,10 @@ pub struct SessionMeta { pub self_node_id: NodeId, /// Session threshold. pub threshold: usize, -} - -/// Errors which can occur during encryption/decryption session -#[derive(Clone, Debug, PartialEq)] -pub enum Error { - /// Invalid node address has been passed. - InvalidNodeAddress, - /// Invalid node id has been passed. - InvalidNodeId, - /// Session with the given id already exists. - DuplicateSessionId, - /// Session with the same id already completed. - CompletedSessionId, - /// Session is not ready to start yet (required data is not ready). - NotStartedSessionId, - /// Session with the given id is unknown. - InvalidSessionId, - /// Invalid number of nodes. - /// There must be at least two nodes participating in encryption. - /// There must be at least one node participating in decryption. - InvalidNodesCount, - /// Node which is required to start encryption/decryption session is not a part of cluster. - InvalidNodesConfiguration, - /// Invalid threshold value has been passed. - /// Threshold value must be in [0; n - 1], where n is a number of nodes participating in the encryption. - InvalidThreshold, - /// Current state of encryption/decryption session does not allow to proceed request. - /// Reschedule this request for later processing. - TooEarlyForRequest, - /// Current state of encryption/decryption session does not allow to proceed request. - /// This means that either there is some comm-failure or node is misbehaving/cheating. - InvalidStateForRequest, - /// Request cannot be sent/received from this node. - InvalidNodeForRequest, - /// Message or some data in the message was recognized as invalid. - /// This means that node is misbehaving/cheating. - InvalidMessage, - /// Message version is not supported. - InvalidMessageVersion, - /// Message is invalid because of replay-attack protection. - ReplayProtection, - /// Connection to node, required for this session is not established. - NodeDisconnected, - /// Node is missing requested key share. - MissingKeyShare, - /// Cryptographic error. - EthKey(String), - /// I/O error has occured. - Io(String), - /// Deserialization error has occured. - Serde(String), - /// Key storage error. - KeyStorage(String), - /// Consensus is unreachable. - ConsensusUnreachable, - /// Acl storage error. - AccessDenied, - /// Can't start session, because exclusive session is active. - ExclusiveSessionActive, - /// Can't start exclusive session, because there are other active sessions. - HasActiveSessions, - /// Insufficient requester data. - InsufficientRequesterData, -} - -impl From for Error { - fn from(err: ethkey::Error) -> Self { - Error::EthKey(err.into()) - } -} - -impl From for Error { - fn from(err: ethcrypto::Error) -> Self { - Error::EthKey(err.into()) - } -} - -impl From for Error { - fn from(err: IoError) -> Self { - Error::Io(err.to_string()) - } -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Error::InvalidNodeAddress => write!(f, "invalid node address has been passed"), - Error::InvalidNodeId => write!(f, "invalid node id has been passed"), - Error::DuplicateSessionId => write!(f, "session with the same id is already registered"), - Error::CompletedSessionId => write!(f, "session with the same id is already completed"), - Error::NotStartedSessionId => write!(f, "not enough data to start session with the given id"), - Error::InvalidSessionId => write!(f, "invalid session id has been passed"), - Error::InvalidNodesCount => write!(f, "invalid nodes count"), - Error::InvalidNodesConfiguration => write!(f, "invalid nodes configuration"), - Error::InvalidThreshold => write!(f, "invalid threshold value has been passed"), - Error::TooEarlyForRequest => write!(f, "session is not yet ready to process this request"), - Error::InvalidStateForRequest => write!(f, "session is in invalid state for processing this request"), - Error::InvalidNodeForRequest => write!(f, "invalid node for this request"), - Error::InvalidMessage => write!(f, "invalid message is received"), - Error::InvalidMessageVersion => write!(f, "unsupported message is received"), - Error::ReplayProtection => write!(f, "replay message is received"), - Error::NodeDisconnected => write!(f, "node required for this operation is currently disconnected"), - Error::MissingKeyShare => write!(f, "requested key share version is not found"), - Error::EthKey(ref e) => write!(f, "cryptographic error {}", e), - Error::Io(ref e) => write!(f, "i/o error {}", e), - Error::Serde(ref e) => write!(f, "serde error {}", e), - Error::KeyStorage(ref e) => write!(f, "key storage error {}", e), - Error::ConsensusUnreachable => write!(f, "Consensus unreachable"), - Error::AccessDenied => write!(f, "Access denied"), - Error::ExclusiveSessionActive => write!(f, "Exclusive session active"), - Error::HasActiveSessions => write!(f, "Unable to start exclusive session"), - Error::InsufficientRequesterData => write!(f, "Insufficient requester data"), - } - } -} - -impl Into for Error { - fn into(self) -> String { - format!("{}", self) - } + /// Count of all configured key server nodes (valid at session start time). + pub configured_nodes_count: usize, + /// Count of all connected key server nodes (valid at session start time). + pub connected_nodes_count: usize, } mod admin_sessions; diff --git a/secret_store/src/key_server_cluster/net/accept_connection.rs b/secret_store/src/key_server_cluster/net/accept_connection.rs index d85e492dd7b9f0a608f795b40efc56d690fa838c..3565ea3d0fd81bbab49833ccbb3dc63c09e1ece7 100644 --- a/secret_store/src/key_server_cluster/net/accept_connection.rs +++ b/secret_store/src/key_server_cluster/net/accept_connection.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/secret_store/src/key_server_cluster/net/connect.rs b/secret_store/src/key_server_cluster/net/connect.rs index 7515494e44992bf151ba9090aeadfdf901463c7c..8b93479f97d81ac65fb5d84ddaed2b06f9986018 100644 --- a/secret_store/src/key_server_cluster/net/connect.rs +++ b/secret_store/src/key_server_cluster/net/connect.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/secret_store/src/key_server_cluster/net/connection.rs b/secret_store/src/key_server_cluster/net/connection.rs index 577f5828f659205e213c793229ee250f5d7822d6..7776e97a74e9d9f1383ac845d339966e388e17ce 100644 --- a/secret_store/src/key_server_cluster/net/connection.rs +++ b/secret_store/src/key_server_cluster/net/connection.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/secret_store/src/key_server_cluster/net/mod.rs b/secret_store/src/key_server_cluster/net/mod.rs index 6abf83ceb8e1a4323588e224cac539af7539cfdd..e76f4f476c698427445a589a017360eb51306e0e 100644 --- a/secret_store/src/key_server_cluster/net/mod.rs +++ b/secret_store/src/key_server_cluster/net/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/secret_store/src/key_server_set.rs b/secret_store/src/key_server_set.rs index f069368b044d237c9cfa342572aed384773395c5..7bae27017a399c7172c7086652e7596c7b2fa4a3 100644 --- a/secret_store/src/key_server_set.rs +++ b/secret_store/src/key_server_set.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -17,16 +17,15 @@ use std::sync::Arc; use std::net::SocketAddr; use std::collections::{BTreeMap, HashSet}; +use std::time::Duration; use parking_lot::Mutex; -use ethcore::client::{Client, BlockChainClient, BlockId, ChainNotify, CallContract, RegistryInfo}; -use ethcore::filter::Filter; +use ethcore::client::{Client, BlockChainClient, BlockId, ChainNotify, ChainRoute, CallContract}; use ethkey::public_to_address; -use hash::keccak; use ethereum_types::{H256, Address}; use bytes::Bytes; -use types::all::{Error, Public, NodeAddress, NodeId}; +use types::{Error, Public, NodeAddress, NodeId}; use trusted_client::TrustedClient; -use {NodeKeyPair}; +use {NodeKeyPair, ContractAddress}; use_contract!(key_server, "KeyServerSet", "res/key_server_set.json"); @@ -37,22 +36,6 @@ const MIGRATION_CONFIRMATIONS_REQUIRED: u64 = 5; /// Number of blocks before the same-migration transaction (be it start or confirmation) will be retried. const TRANSACTION_RETRY_INTERVAL_BLOCKS: u64 = 30; -/// Key server has been added to the set. -const ADDED_EVENT_NAME: &'static [u8] = &*b"KeyServerAdded(address)"; -/// Key server has been removed from the set. -const REMOVED_EVENT_NAME: &'static [u8] = &*b"KeyServerRemoved(address)"; -/// Migration has started. -const MIGRATION_STARTED_EVENT_NAME: &'static [u8] = &*b"MigrationStarted()"; -/// Migration has completed. -const MIGRATION_COMPLETED_EVENT_NAME: &'static [u8] = &*b"MigrationCompleted()"; - -lazy_static! { - static ref ADDED_EVENT_NAME_HASH: H256 = keccak(ADDED_EVENT_NAME); - static ref REMOVED_EVENT_NAME_HASH: H256 = keccak(REMOVED_EVENT_NAME); - static ref MIGRATION_STARTED_EVENT_NAME_HASH: H256 = keccak(MIGRATION_STARTED_EVENT_NAME); - static ref MIGRATION_COMPLETED_EVENT_NAME_HASH: H256 = keccak(MIGRATION_COMPLETED_EVENT_NAME); -} - #[derive(Default, Debug, Clone, PartialEq)] /// Key Server Set state. pub struct KeyServerSetSnapshot { @@ -79,6 +62,8 @@ pub struct KeyServerSetMigration { /// Key Server Set pub trait KeyServerSet: Send + Sync { + /// Is this node currently isolated from the set? + fn is_isolated(&self) -> bool; /// Get server set state. fn snapshot(&self) -> KeyServerSetSnapshot; /// Start migration. @@ -115,7 +100,9 @@ struct PreviousMigrationTransaction { struct CachedContract { /// Blockchain client. client: TrustedClient, - /// Contract address. + /// Contract address source. + contract_address_source: Option, + /// Current contract address. contract_address: Option
, /// Contract interface. contract: key_server::KeyServerSet, @@ -134,19 +121,23 @@ struct CachedContract { } impl OnChainKeyServerSet { - pub fn new(trusted_client: TrustedClient, self_key_pair: Arc, auto_migrate_enabled: bool, key_servers: BTreeMap) -> Result, Error> { + pub fn new(trusted_client: TrustedClient, contract_address_source: Option, self_key_pair: Arc, auto_migrate_enabled: bool, key_servers: BTreeMap) -> Result, Error> { let client = trusted_client.get_untrusted(); let key_server_set = Arc::new(OnChainKeyServerSet { - contract: Mutex::new(CachedContract::new(trusted_client, self_key_pair, auto_migrate_enabled, key_servers)?), + contract: Mutex::new(CachedContract::new(trusted_client, contract_address_source, self_key_pair, auto_migrate_enabled, key_servers)?), }); client - .ok_or(Error::Internal("Constructing OnChainKeyServerSet without active Client".into()))? + .ok_or_else(|| Error::Internal("Constructing OnChainKeyServerSet without active Client".into()))? .add_notify(key_server_set.clone()); Ok(key_server_set) } } impl KeyServerSet for OnChainKeyServerSet { + fn is_isolated(&self) -> bool { + self.contract.lock().is_isolated() + } + fn snapshot(&self) -> KeyServerSetSnapshot { self.contract.lock().snapshot() } @@ -161,7 +152,9 @@ impl KeyServerSet for OnChainKeyServerSet { } impl ChainNotify for OnChainKeyServerSet { - fn new_blocks(&self, _imported: Vec, _invalid: Vec, enacted: Vec, retracted: Vec, _sealed: Vec, _proposed: Vec, _duration: u64) { + fn new_blocks(&self, _imported: Vec, _invalid: Vec, route: ChainRoute, _sealed: Vec, _proposed: Vec, _duration: Duration) { + let (enacted, retracted) = route.into_enacted_retracted(); + if !enacted.is_empty() || !retracted.is_empty() { self.contract.lock().update(enacted, retracted) } @@ -240,16 +233,21 @@ impl ) -> Result, String>> KeyServerSubset for NewKeySe } impl CachedContract { - pub fn new(client: TrustedClient, self_key_pair: Arc, auto_migrate_enabled: bool, key_servers: BTreeMap) -> Result { - let server_set = key_servers.into_iter() - .map(|(p, addr)| { - let addr = format!("{}:{}", addr.address, addr.port).parse() - .map_err(|err| Error::Internal(format!("error parsing node address: {}", err)))?; - Ok((p, addr)) - }) - .collect::, Error>>()?; - Ok(CachedContract { + pub fn new(client: TrustedClient, contract_address_source: Option, self_key_pair: Arc, auto_migrate_enabled: bool, key_servers: BTreeMap) -> Result { + let server_set = match contract_address_source.is_none() { + true => key_servers.into_iter() + .map(|(p, addr)| { + let addr = format!("{}:{}", addr.address, addr.port).parse() + .map_err(|err| Error::Internal(format!("error parsing node address: {}", err)))?; + Ok((p, addr)) + }) + .collect::, Error>>()?, + false => Default::default(), + }; + + let mut contract = CachedContract { client: client, + contract_address_source: contract_address_source, contract_address: None, contract: key_server::KeyServerSet::default(), auto_migrate_enabled: auto_migrate_enabled, @@ -262,19 +260,46 @@ impl CachedContract { ..Default::default() }, self_key_pair: self_key_pair, - }) + }; + contract.update_contract_address(); + + Ok(contract) + } + + pub fn update_contract_address(&mut self) { + if let Some(ref contract_address_source) = self.contract_address_source { + let contract_address = self.client.read_contract_address(KEY_SERVER_SET_CONTRACT_REGISTRY_NAME.into(), contract_address_source); + if contract_address != self.contract_address { + trace!(target: "secretstore", "{}: Configuring for key server set contract from address {:?}", + self.self_key_pair.public(), contract_address); + + self.contract_address = contract_address; + } + } } pub fn update(&mut self, enacted: Vec, retracted: Vec) { + // no need to update when servers set is hardcoded + if self.contract_address_source.is_none() { + return; + } + if let Some(client) = self.client.get() { // read new snapshot from reqistry (if something has chnaged) - self.read_from_registry_if_required(&*client, enacted, retracted); + if !enacted.is_empty() || !retracted.is_empty() { + self.update_contract_address(); + self.read_from_registry(&*client); + } // update number of confirmations (if there's future new set) self.update_number_of_confirmations_if_required(&*client); } } + fn is_isolated(&self) -> bool { + !self.snapshot.current_set.contains_key(self.self_key_pair.public()) + } + fn snapshot(&self) -> KeyServerSetSnapshot { self.snapshot.clone() } @@ -291,12 +316,11 @@ impl CachedContract { let transaction_data = self.contract.functions().start_migration().input(migration_id); // send transaction - if let Err(error) = client.transact_contract(*contract_address, transaction_data) { - warn!(target: "secretstore_net", "{}: failed to submit auto-migration start transaction: {}", - self.self_key_pair.public(), error); - } else { - trace!(target: "secretstore_net", "{}: sent auto-migration start transaction", - self.self_key_pair.public()); + match self.client.transact_contract(*contract_address, transaction_data) { + Ok(_) => trace!(target: "secretstore_net", "{}: sent auto-migration start transaction", + self.self_key_pair.public()), + Err(error) => warn!(target: "secretstore_net", "{}: failed to submit auto-migration start transaction: {}", + self.self_key_pair.public(), error), } } } @@ -313,61 +337,22 @@ impl CachedContract { let transaction_data = self.contract.functions().confirm_migration().input(migration_id); // send transaction - if let Err(error) = client.transact_contract(contract_address, transaction_data) { - warn!(target: "secretstore_net", "{}: failed to submit auto-migration confirmation transaction: {}", - self.self_key_pair.public(), error); - } else { - trace!(target: "secretstore_net", "{}: sent auto-migration confirm transaction", - self.self_key_pair.public()); + match self.client.transact_contract(contract_address, transaction_data) { + Ok(_) => trace!(target: "secretstore_net", "{}: sent auto-migration confirm transaction", + self.self_key_pair.public()), + Err(error) => warn!(target: "secretstore_net", "{}: failed to submit auto-migration confirmation transaction: {}", + self.self_key_pair.public(), error), } } } - fn read_from_registry_if_required(&mut self, client: &Client, enacted: Vec, retracted: Vec) { - // read new contract from registry - let new_contract_addr = client.registry_address(KEY_SERVER_SET_CONTRACT_REGISTRY_NAME.to_owned(), BlockId::Latest); - - // new contract installed => read nodes set from the contract - if self.contract_address.as_ref() != new_contract_addr.as_ref() { - self.read_from_registry(&*client, new_contract_addr); - return; - } - - // check for contract events - let is_set_changed = self.contract_address.is_some() && enacted.iter() - .chain(retracted.iter()) - .any(|block_hash| !client.logs(Filter { - from_block: BlockId::Hash(block_hash.clone()), - to_block: BlockId::Hash(block_hash.clone()), - address: self.contract_address.map(|address| vec![address]), - topics: vec![ - Some(vec![*ADDED_EVENT_NAME_HASH, *REMOVED_EVENT_NAME_HASH, - *MIGRATION_STARTED_EVENT_NAME_HASH, *MIGRATION_COMPLETED_EVENT_NAME_HASH]), - None, - None, - None, - ], - limit: Some(1), - }).is_empty()); - - // to simplify processing - just re-read the whole nodes set from the contract - if is_set_changed { - self.read_from_registry(&*client, new_contract_addr); - } - } - - fn read_from_registry(&mut self, client: &Client, new_contract_address: Option
) { - if let Some(ref contract_addr) = new_contract_address { - trace!(target: "secretstore", "Configuring for key server set contract from {}", contract_addr); - } - self.contract_address = new_contract_address; - + fn read_from_registry(&mut self, client: &Client) { let contract_address = match self.contract_address { Some(contract_address) => contract_address, None => { // no contract installed => empty snapshot // WARNING: after restart current_set will be reset to the set from configuration file - // even though we have reset to empty set here. We are not considerning this as an issue + // even though we have reset to empty set here. We are not considering this as an issue // because it is actually the issue of administrator. self.snapshot = Default::default(); self.future_new_set = None; @@ -501,7 +486,7 @@ fn update_future_set(future_new_set: &mut Option, new_snapshot: &m return; } - // new no migration is required => no need to delay visibility + // no migration is required => no need to delay visibility if !is_migration_required(&new_snapshot.current_set, &new_snapshot.new_set) { *future_new_set = None; return; @@ -536,7 +521,7 @@ fn update_number_of_confirmations H256, F2: Fn(H256) -> Option> // not enough confirmations => do nothing Some(_) => return, // if number of confirmations is None, then reorg has happened && we need to reset block - // (some more intelligent startegy is possible, but let's stick to simplest one) + // (some more intelligent strategy is possible, but let's stick to simplest one) None => { future_new_set.block = latest_block(); return; @@ -550,12 +535,11 @@ fn update_number_of_confirmations H256, F2: Fn(H256) -> Option> } fn update_last_transaction_block(client: &Client, migration_id: &H256, previous_transaction: &mut Option) -> bool { - // TODO [Reliability]: add the same mechanism to the contract listener, if accepted let last_block = client.block_number(BlockId::Latest).unwrap_or_default(); match previous_transaction.as_ref() { - // no previous transaction => send immideately + // no previous transaction => send immediately None => (), - // previous transaction has been sent for other migration process => send immideately + // previous transaction has been sent for other migration process => send immediately Some(tx) if tx.migration_id != *migration_id => (), // if we have sent the same type of transaction recently => do nothing (hope it will be mined eventually) // if we have sent the same transaction some time ago => @@ -564,7 +548,6 @@ fn update_last_transaction_block(client: &Client, migration_id: &H256, previous_ // or the transaction has been removed from the queue (and never reached any miner node) // if we have restarted after sending tx => assume we have never sent it Some(tx) => { - let last_block = client.block_number(BlockId::Latest).unwrap_or_default(); if tx.block > last_block || last_block - tx.block < TRANSACTION_RETRY_INTERVAL_BLOCKS { return false; } @@ -600,18 +583,24 @@ pub mod tests { #[derive(Default)] pub struct MapKeyServerSet { + is_isolated: bool, nodes: BTreeMap, } impl MapKeyServerSet { - pub fn new(nodes: BTreeMap) -> Self { + pub fn new(is_isolated: bool, nodes: BTreeMap) -> Self { MapKeyServerSet { + is_isolated: is_isolated, nodes: nodes, } } } impl KeyServerSet for MapKeyServerSet { + fn is_isolated(&self) -> bool { + self.is_isolated + } + fn snapshot(&self) -> KeyServerSetSnapshot { KeyServerSetSnapshot { current_set: self.nodes.clone(), diff --git a/secret_store/src/key_storage.rs b/secret_store/src/key_storage.rs index 4429500db13d9a3b7a6c0200b69b41a77dd4122c..f19630c5c6ef2a4506d6addf2707110fff12211f 100644 --- a/secret_store/src/key_storage.rs +++ b/secret_store/src/key_storage.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -14,14 +14,14 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use std::path::PathBuf; use std::collections::BTreeMap; +use std::sync::Arc; use serde_json; use tiny_keccak::Keccak; use ethereum_types::{H256, Address}; use ethkey::{Secret, Public, public_to_address}; -use kvdb_rocksdb::{Database, DatabaseIterator}; -use types::all::{Error, ServiceConfiguration, ServerKeyId, NodeId}; +use kvdb::KeyValueDB; +use types::{Error, ServerKeyId, NodeId}; use serialization::{SerializablePublic, SerializableSecret, SerializableH256, SerializableAddress}; /// Key of version value. @@ -82,17 +82,17 @@ pub trait KeyStorage: Send + Sync { /// Persistent document encryption keys storage pub struct PersistentKeyStorage { - db: Database, + db: Arc, } /// Persistent document encryption keys storage iterator pub struct PersistentKeyStorageIterator<'a> { - iter: Option>, + iter: Box, Box<[u8]>)> + 'a>, } /// V0 of encrypted key share, as it is stored by key storage on the single key server. #[derive(Serialize, Deserialize)] -struct SerializableDocumentKeyShareV0 { +pub struct SerializableDocumentKeyShareV0 { /// Decryption threshold (at least threshold + 1 nodes are required to decrypt data). pub threshold: usize, /// Nodes ids numbers. @@ -108,7 +108,7 @@ struct SerializableDocumentKeyShareV0 { /// V1 of encrypted key share, as it is stored by key storage on the single key server. #[derive(Serialize, Deserialize)] struct SerializableDocumentKeyShareV1 { - /// Authore of the entry. + /// Author of the entry. pub author: SerializablePublic, /// Decryption threshold (at least threshold + 1 nodes are required to decrypt data). pub threshold: usize, @@ -172,12 +172,7 @@ type SerializableDocumentKeyShareVersionV3 = SerializableDocumentKeyShareVersion impl PersistentKeyStorage { /// Create new persistent document encryption keys storage - pub fn new(config: &ServiceConfiguration) -> Result { - let mut db_path = PathBuf::from(&config.data_path); - db_path.push("db"); - let db_path = db_path.to_str().ok_or_else(|| Error::Database("Invalid secretstore path".to_owned()))?; - - let db = Database::open_default(&db_path)?; + pub fn new(db: Arc) -> Result { let db = upgrade_db(db)?; Ok(PersistentKeyStorage { @@ -186,14 +181,14 @@ impl PersistentKeyStorage { } } -fn upgrade_db(db: Database) -> Result { +fn upgrade_db(db: Arc) -> Result, Error> { let version = db.get(None, DB_META_KEY_VERSION)?; let version = version.and_then(|v| v.get(0).cloned()).unwrap_or(0); match version { 0 => { let mut batch = db.transaction(); batch.put(None, DB_META_KEY_VERSION, &[CURRENT_VERSION]); - for (db_key, db_value) in db.iter(None).into_iter().flat_map(|inner| inner).filter(|&(ref k, _)| **k != *DB_META_KEY_VERSION) { + for (db_key, db_value) in db.iter(None).into_iter().filter(|&(ref k, _)| **k != *DB_META_KEY_VERSION) { let v0_key = serde_json::from_slice::(&db_value).map_err(|e| Error::Database(e.to_string()))?; let current_key = CurrentSerializableDocumentKeyShare { // author is used in separate generation + encrypt sessions. @@ -218,7 +213,7 @@ fn upgrade_db(db: Database) -> Result { 1 => { let mut batch = db.transaction(); batch.put(None, DB_META_KEY_VERSION, &[CURRENT_VERSION]); - for (db_key, db_value) in db.iter(None).into_iter().flat_map(|inner| inner).filter(|&(ref k, _)| **k != *DB_META_KEY_VERSION) { + for (db_key, db_value) in db.iter(None).into_iter().filter(|&(ref k, _)| **k != *DB_META_KEY_VERSION) { let v1_key = serde_json::from_slice::(&db_value).map_err(|e| Error::Database(e.to_string()))?; let current_key = CurrentSerializableDocumentKeyShare { author: public_to_address(&v1_key.author).into(), // added in v1 + changed in v3 @@ -241,7 +236,7 @@ fn upgrade_db(db: Database) -> Result { 2 => { let mut batch = db.transaction(); batch.put(None, DB_META_KEY_VERSION, &[CURRENT_VERSION]); - for (db_key, db_value) in db.iter(None).into_iter().flat_map(|inner| inner).filter(|&(ref k, _)| **k != *DB_META_KEY_VERSION) { + for (db_key, db_value) in db.iter(None).into_iter().filter(|&(ref k, _)| **k != *DB_META_KEY_VERSION) { let v2_key = serde_json::from_slice::(&db_value).map_err(|e| Error::Database(e.to_string()))?; let current_key = CurrentSerializableDocumentKeyShare { author: public_to_address(&v2_key.author).into(), // changed in v3 @@ -319,11 +314,10 @@ impl<'a> Iterator for PersistentKeyStorageIterator<'a> { type Item = (ServerKeyId, DocumentKeyShare); fn next(&mut self) -> Option<(ServerKeyId, DocumentKeyShare)> { - self.iter.as_mut() - .and_then(|iter| iter.next() - .and_then(|(db_key, db_val)| serde_json::from_slice::(&db_val) - .ok() - .map(|key| ((*db_key).into(), key.into())))) + self.iter.as_mut().next() + .and_then(|(db_key, db_val)| serde_json::from_slice::(&db_val) + .ok() + .map(|key| ((*db_key).into(), key.into()))) } } @@ -354,7 +348,6 @@ impl DocumentKeyShareVersion { } } - /// Calculate hash of given version data. pub fn data_hash<'a, I>(id_numbers: I) -> H256 where I: Iterator { let mut nodes_keccak = Keccak::new_keccak256(); @@ -417,14 +410,15 @@ impl From for DocumentKeyShare { pub mod tests { extern crate tempdir; - use std::collections::{BTreeMap, HashMap}; + use std::collections::HashMap; + use std::sync::Arc; use parking_lot::RwLock; use serde_json; use self::tempdir::TempDir; use ethereum_types::{Address, H256}; use ethkey::{Random, Generator, Public, Secret, public_to_address}; use kvdb_rocksdb::Database; - use types::all::{Error, NodeAddress, ServiceConfiguration, ClusterConfiguration, ServerKeyId}; + use types::{Error, ServerKeyId}; use super::{DB_META_KEY_VERSION, CURRENT_VERSION, KeyStorage, PersistentKeyStorage, DocumentKeyShare, DocumentKeyShareVersion, CurrentSerializableDocumentKeyShare, upgrade_db, SerializableDocumentKeyShareV0, SerializableDocumentKeyShareV1, SerializableDocumentKeyShareV2, SerializableDocumentKeyShareVersionV2}; @@ -472,24 +466,6 @@ pub mod tests { #[test] fn persistent_key_storage() { let tempdir = TempDir::new("").unwrap(); - let config = ServiceConfiguration { - listener_address: None, - service_contract_address: None, - acl_check_enabled: true, - data_path: tempdir.path().display().to_string(), - cluster_config: ClusterConfiguration { - threads: 1, - listener_address: NodeAddress { - address: "0.0.0.0".to_owned(), - port: 8083, - }, - nodes: BTreeMap::new(), - allow_connecting_to_higher_nodes: false, - admin_public: None, - auto_migrate_enabled: false, - }, - }; - let key1 = ServerKeyId::from(1); let value1 = DocumentKeyShare { author: Default::default(), @@ -522,7 +498,9 @@ pub mod tests { }; let key3 = ServerKeyId::from(3); - let key_storage = PersistentKeyStorage::new(&config).unwrap(); + let db = Database::open_default(&tempdir.path().display().to_string()).unwrap(); + + let key_storage = PersistentKeyStorage::new(Arc::new(db)).unwrap(); key_storage.insert(key1.clone(), value1.clone()).unwrap(); key_storage.insert(key2.clone(), value2.clone()).unwrap(); assert_eq!(key_storage.get(&key1), Ok(Some(value1.clone()))); @@ -530,7 +508,9 @@ pub mod tests { assert_eq!(key_storage.get(&key3), Ok(None)); drop(key_storage); - let key_storage = PersistentKeyStorage::new(&config).unwrap(); + let db = Database::open_default(&tempdir.path().display().to_string()).unwrap(); + + let key_storage = PersistentKeyStorage::new(Arc::new(db)).unwrap(); assert_eq!(key_storage.get(&key1), Ok(Some(value1))); assert_eq!(key_storage.get(&key2), Ok(Some(value2))); assert_eq!(key_storage.get(&key3), Ok(None)); @@ -559,7 +539,7 @@ pub mod tests { } // upgrade database - let db = upgrade_db(db).unwrap(); + let db = upgrade_db(Arc::new(db)).unwrap(); // check upgrade assert_eq!(db.get(None, DB_META_KEY_VERSION).unwrap().unwrap()[0], CURRENT_VERSION); @@ -602,7 +582,7 @@ pub mod tests { } // upgrade database - let db = upgrade_db(db).unwrap(); + let db = upgrade_db(Arc::new(db)).unwrap(); // check upgrade assert_eq!(db.get(None, DB_META_KEY_VERSION).unwrap().unwrap()[0], CURRENT_VERSION); @@ -650,7 +630,7 @@ pub mod tests { } // upgrade database - let db = upgrade_db(db).unwrap(); + let db = upgrade_db(Arc::new(db)).unwrap(); // check upgrade assert_eq!(db.get(None, DB_META_KEY_VERSION).unwrap().unwrap()[0], CURRENT_VERSION); diff --git a/secret_store/src/lib.rs b/secret_store/src/lib.rs index 9d89ba0c633282ddfc100bb8fc63d686bee88137..74cde2c5a95fc641835007138023cf89ce406f13 100644 --- a/secret_store/src/lib.rs +++ b/secret_store/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -18,21 +18,22 @@ extern crate byteorder; extern crate ethabi; extern crate ethcore; extern crate ethcore_bytes as bytes; +extern crate ethcore_crypto as crypto; extern crate ethcore_logger as logger; -extern crate ethcrypto; +extern crate ethcore_sync as sync; +extern crate ethcore_transaction as transaction; extern crate ethereum_types; extern crate ethkey; -extern crate ethsync; extern crate futures_cpupool; extern crate hyper; extern crate keccak_hash as hash; extern crate kvdb; -extern crate kvdb_rocksdb; extern crate parking_lot; extern crate rustc_hex; extern crate serde; extern crate serde_json; extern crate tiny_keccak; +extern crate tokio; extern crate tokio_core; extern crate tokio_io; extern crate tokio_proto; @@ -52,8 +53,12 @@ extern crate lazy_static; #[macro_use] extern crate log; +#[cfg(test)] +extern crate kvdb_rocksdb; + mod key_server_cluster; mod types; +mod helpers; mod traits; mod acl_storage; @@ -66,46 +71,96 @@ mod listener; mod trusted_client; use std::sync::Arc; +use kvdb::KeyValueDB; use ethcore::client::Client; -use ethsync::SyncProvider; +use ethcore::miner::Miner; +use sync::SyncProvider; -pub use types::all::{ServerKeyId, EncryptedDocumentKey, RequestSignature, Public, +pub use types::{ServerKeyId, EncryptedDocumentKey, RequestSignature, Public, Error, NodeAddress, ContractAddress, ServiceConfiguration, ClusterConfiguration}; pub use traits::{NodeKeyPair, KeyServer}; pub use self::node_key_pair::{PlainNodeKeyPair, KeyStoreNodeKeyPair}; /// Start new key server instance -pub fn start(client: Arc, sync: Arc, self_key_pair: Arc, config: ServiceConfiguration) -> Result, Error> { - let trusted_client = trusted_client::TrustedClient::new(client.clone(), sync); - let acl_storage: Arc = if config.acl_check_enabled { - acl_storage::OnChainAclStorage::new(trusted_client.clone())? - } else { - Arc::new(acl_storage::DummyAclStorage::default()) - }; - - let key_server_set = key_server_set::OnChainKeyServerSet::new(trusted_client.clone(), self_key_pair.clone(), - config.cluster_config.auto_migrate_enabled, config.cluster_config.nodes.clone())?; - let key_storage = Arc::new(key_storage::PersistentKeyStorage::new(&config)?); - let key_server = Arc::new(key_server::KeyServerImpl::new(&config.cluster_config, key_server_set.clone(), self_key_pair.clone(), acl_storage, key_storage.clone())?); +pub fn start(client: Arc, sync: Arc, miner: Arc, self_key_pair: Arc, mut config: ServiceConfiguration, db: Arc) -> Result, Error> { + let trusted_client = trusted_client::TrustedClient::new(self_key_pair.clone(), client.clone(), sync, miner); + let acl_storage: Arc = match config.acl_check_contract_address.take() { + Some(acl_check_contract_address) => acl_storage::OnChainAclStorage::new(trusted_client.clone(), acl_check_contract_address)?, + None => Arc::new(acl_storage::DummyAclStorage::default()), + }; + + let key_server_set = key_server_set::OnChainKeyServerSet::new(trusted_client.clone(), config.cluster_config.key_server_set_contract_address.take(), + self_key_pair.clone(), config.cluster_config.auto_migrate_enabled, config.cluster_config.nodes.clone())?; + let key_storage = Arc::new(key_storage::PersistentKeyStorage::new(db)?); + let key_server = Arc::new(key_server::KeyServerImpl::new(&config.cluster_config, key_server_set.clone(), self_key_pair.clone(), acl_storage.clone(), key_storage.clone())?); let cluster = key_server.cluster(); + let key_server: Arc = key_server; - // prepare listeners + // prepare HTTP listener let http_listener = match config.listener_address { - Some(listener_address) => Some(listener::http_listener::KeyServerHttpListener::start(listener_address, key_server.clone())?), + Some(listener_address) => Some(listener::http_listener::KeyServerHttpListener::start(listener_address, Arc::downgrade(&key_server))?), None => None, }; - let contract_listener = config.service_contract_address.map(|service_contract_address| { - let service_contract = Arc::new(listener::service_contract::OnChainServiceContract::new(trusted_client, service_contract_address, self_key_pair.clone())); - let contract_listener = listener::service_contract_listener::ServiceContractListener::new(listener::service_contract_listener::ServiceContractListenerParams { - contract: service_contract, - key_server: key_server.clone(), - self_key_pair: self_key_pair, - key_server_set: key_server_set, - cluster: cluster, - key_storage: key_storage, - }); - client.add_notify(contract_listener.clone()); - contract_listener - }); + + // prepare service contract listeners + let create_service_contract = |address, name, api_mask| + Arc::new(listener::service_contract::OnChainServiceContract::new( + api_mask, + trusted_client.clone(), + name, + address, + self_key_pair.clone())); + + let mut contracts: Vec> = Vec::new(); + config.service_contract_address.map(|address| + create_service_contract(address, + listener::service_contract::SERVICE_CONTRACT_REGISTRY_NAME.to_owned(), + listener::ApiMask::all())) + .map(|l| contracts.push(l)); + config.service_contract_srv_gen_address.map(|address| + create_service_contract(address, + listener::service_contract::SRV_KEY_GEN_SERVICE_CONTRACT_REGISTRY_NAME.to_owned(), + listener::ApiMask { server_key_generation_requests: true, ..Default::default() })) + .map(|l| contracts.push(l)); + config.service_contract_srv_retr_address.map(|address| + create_service_contract(address, + listener::service_contract::SRV_KEY_RETR_SERVICE_CONTRACT_REGISTRY_NAME.to_owned(), + listener::ApiMask { server_key_retrieval_requests: true, ..Default::default() })) + .map(|l| contracts.push(l)); + config.service_contract_doc_store_address.map(|address| + create_service_contract(address, + listener::service_contract::DOC_KEY_STORE_SERVICE_CONTRACT_REGISTRY_NAME.to_owned(), + listener::ApiMask { document_key_store_requests: true, ..Default::default() })) + .map(|l| contracts.push(l)); + config.service_contract_doc_sretr_address.map(|address| + create_service_contract(address, + listener::service_contract::DOC_KEY_SRETR_SERVICE_CONTRACT_REGISTRY_NAME.to_owned(), + listener::ApiMask { document_key_shadow_retrieval_requests: true, ..Default::default() })) + .map(|l| contracts.push(l)); + + let contract: Option> = match contracts.len() { + 0 => None, + 1 => Some(contracts.pop().expect("contract.len() is 1; qed")), + _ => Some(Arc::new(listener::service_contract_aggregate::OnChainServiceContractAggregate::new(contracts))), + }; + + let contract_listener = match contract { + Some(contract) => Some({ + let listener = listener::service_contract_listener::ServiceContractListener::new( + listener::service_contract_listener::ServiceContractListenerParams { + contract: contract, + self_key_pair: self_key_pair.clone(), + key_server_set: key_server_set, + acl_storage: acl_storage, + cluster: cluster, + key_storage: key_storage, + } + )?; + client.add_notify(listener.clone()); + listener + }), + None => None, + }; + Ok(Box::new(listener::Listener::new(key_server, http_listener, contract_listener))) } diff --git a/secret_store/src/listener/http_listener.rs b/secret_store/src/listener/http_listener.rs index 4d34f984bfc6eaa6724ebca372617933431907aa..5aa82a1cbda014b31a64e676823db655cee1dcca 100644 --- a/secret_store/src/listener/http_listener.rs +++ b/secret_store/src/listener/http_listener.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -15,35 +15,35 @@ // along with Parity. If not, see . use std::collections::BTreeSet; -use std::io::Read; -use std::sync::Arc; -use hyper::header; -use hyper::uri::RequestUri; -use hyper::method::Method as HttpMethod; -use hyper::status::StatusCode as HttpStatusCode; -use hyper::server::{Server as HttpServer, Request as HttpRequest, Response as HttpResponse, Handler as HttpHandler, - Listening as HttpListening}; +use std::sync::{Arc, Weak}; +use hyper::{self, header, Chunk, Uri, Request as HttpRequest, Response as HttpResponse, Method as HttpMethod, StatusCode as HttpStatusCode}; +use hyper::server::Http; use serde::Serialize; use serde_json; +use tokio; +use tokio::net::TcpListener; +use tokio::runtime::Runtime; +use tokio_service::Service; +use futures::{future, Future, Stream}; use url::percent_encoding::percent_decode; use traits::KeyServer; use serialization::{SerializableEncryptedDocumentKeyShadow, SerializableBytes, SerializablePublic}; -use types::all::{Error, Public, MessageHash, NodeAddress, RequestSignature, ServerKeyId, +use types::{Error, Public, MessageHash, NodeAddress, RequestSignature, ServerKeyId, EncryptedDocumentKey, EncryptedDocumentKeyShadow, NodeId}; /// Key server http-requests listener. Available requests: /// To generate server key: POST /shadow/{server_key_id}/{signature}/{threshold} -/// To store pregenerated encrypted document key: POST /shadow/{server_key_id}/{signature}/{common_point}/{encrypted_key} -/// To generate server && document key: POST /{server_key_id}/{signature}/{threshold} +/// To store pregenerated encrypted document key: POST /shadow/{server_key_id}/{signature}/{common_point}/{encrypted_key} +/// To generate server && document key: POST /{server_key_id}/{signature}/{threshold} /// To get document key: GET /{server_key_id}/{signature} -/// To get document key shadow: GET /shadow/{server_key_id}/{signature} +/// To get document key shadow: GET /shadow/{server_key_id}/{signature} /// To generate Schnorr signature with server key: GET /schnorr/{server_key_id}/{signature}/{message_hash} /// To generate ECDSA signature with server key: GET /ecdsa/{server_key_id}/{signature}/{message_hash} /// To change servers set: POST /admin/servers_set_change/{old_signature}/{new_signature} + BODY: json array of hex-encoded nodes ids pub struct KeyServerHttpListener { - http_server: HttpListening, + _runtime: Runtime, _handler: Arc, } @@ -71,194 +71,228 @@ enum Request { } /// Cloneable http handler +#[derive(Clone)] struct KeyServerHttpHandler { handler: Arc, } /// Shared http handler struct KeyServerSharedHttpHandler { - key_server: Arc, + key_server: Weak, } impl KeyServerHttpListener { /// Start KeyServer http listener - pub fn start(listener_address: NodeAddress, key_server: Arc) -> Result { + pub fn start(listener_address: NodeAddress, key_server: Weak) -> Result { let shared_handler = Arc::new(KeyServerSharedHttpHandler { key_server: key_server, }); - let listener_address = format!("{}:{}", listener_address.address, listener_address.port); - let http_server = HttpServer::http(&listener_address) - .and_then(|http_server| http_server.handle(KeyServerHttpHandler { - handler: shared_handler.clone(), - })).map_err(|err| Error::Hyper(format!("{}", err)))?; + let mut runtime = Runtime::new()?; + let listener_address = format!("{}:{}", listener_address.address, listener_address.port).parse()?; + let listener = TcpListener::bind(&listener_address)?; + + let shared_handler2 = shared_handler.clone(); + + let server = listener.incoming() + .map_err(|e| warn!("Key server listener error: {:?}", e)) + .for_each(move |socket| { + let http: Http = Http::new(); + let serve = http.serve_connection(socket, KeyServerHttpHandler { + handler: shared_handler2.clone(), + }).map(|_| ()).map_err(|e| { + warn!("Key server handler error: {:?}", e); + }); + + tokio::spawn(serve) + }); + + runtime.spawn(server); let listener = KeyServerHttpListener { - http_server: http_server, + _runtime: runtime, _handler: shared_handler, }; + Ok(listener) } } -impl Drop for KeyServerHttpListener { - fn drop(&mut self) { - // ignore error as we are dropping anyway - let _ = self.http_server.close(); +impl KeyServerHttpHandler { + fn process(self, req_method: HttpMethod, req_uri: Uri, path: &str, req_body: &[u8]) -> HttpResponse { + match parse_request(&req_method, &path, &req_body) { + Request::GenerateServerKey(document, signature, threshold) => { + return_server_public_key(&req_uri, self.handler.key_server.upgrade() + .map(|key_server| key_server.generate_key(&document, &signature.into(), threshold)) + .unwrap_or(Err(Error::Internal("KeyServer is already destroyed".into()))) + .map_err(|err| { + warn!(target: "secretstore", "GenerateServerKey request {} has failed with: {}", req_uri, err); + err + })) + }, + Request::StoreDocumentKey(document, signature, common_point, encrypted_document_key) => { + return_empty(&req_uri, self.handler.key_server.upgrade() + .map(|key_server| key_server.store_document_key(&document, &signature.into(), common_point, encrypted_document_key)) + .unwrap_or(Err(Error::Internal("KeyServer is already destroyed".into()))) + .map_err(|err| { + warn!(target: "secretstore", "StoreDocumentKey request {} has failed with: {}", req_uri, err); + err + })) + }, + Request::GenerateDocumentKey(document, signature, threshold) => { + return_document_key(&req_uri, self.handler.key_server.upgrade() + .map(|key_server| key_server.generate_document_key(&document, &signature.into(), threshold)) + .unwrap_or(Err(Error::Internal("KeyServer is already destroyed".into()))) + .map_err(|err| { + warn!(target: "secretstore", "GenerateDocumentKey request {} has failed with: {}", req_uri, err); + err + })) + }, + Request::GetDocumentKey(document, signature) => { + return_document_key(&req_uri, self.handler.key_server.upgrade() + .map(|key_server| key_server.restore_document_key(&document, &signature.into())) + .unwrap_or(Err(Error::Internal("KeyServer is already destroyed".into()))) + .map_err(|err| { + warn!(target: "secretstore", "GetDocumentKey request {} has failed with: {}", req_uri, err); + err + })) + }, + Request::GetDocumentKeyShadow(document, signature) => { + return_document_key_shadow(&req_uri, self.handler.key_server.upgrade() + .map(|key_server| key_server.restore_document_key_shadow(&document, &signature.into())) + .unwrap_or(Err(Error::Internal("KeyServer is already destroyed".into()))) + .map_err(|err| { + warn!(target: "secretstore", "GetDocumentKeyShadow request {} has failed with: {}", req_uri, err); + err + })) + }, + Request::SchnorrSignMessage(document, signature, message_hash) => { + return_message_signature(&req_uri, self.handler.key_server.upgrade() + .map(|key_server| key_server.sign_message_schnorr(&document, &signature.into(), message_hash)) + .unwrap_or(Err(Error::Internal("KeyServer is already destroyed".into()))) + .map_err(|err| { + warn!(target: "secretstore", "SchnorrSignMessage request {} has failed with: {}", req_uri, err); + err + })) + }, + Request::EcdsaSignMessage(document, signature, message_hash) => { + return_message_signature(&req_uri, self.handler.key_server.upgrade() + .map(|key_server| key_server.sign_message_ecdsa(&document, &signature.into(), message_hash)) + .unwrap_or(Err(Error::Internal("KeyServer is already destroyed".into()))) + .map_err(|err| { + warn!(target: "secretstore", "EcdsaSignMessage request {} has failed with: {}", req_uri, err); + err + })) + }, + Request::ChangeServersSet(old_set_signature, new_set_signature, new_servers_set) => { + return_empty(&req_uri, self.handler.key_server.upgrade() + .map(|key_server| key_server.change_servers_set(old_set_signature, new_set_signature, new_servers_set)) + .unwrap_or(Err(Error::Internal("KeyServer is already destroyed".into()))) + .map_err(|err| { + warn!(target: "secretstore", "ChangeServersSet request {} has failed with: {}", req_uri, err); + err + })) + }, + Request::Invalid => { + warn!(target: "secretstore", "Ignoring invalid {}-request {}", req_method, req_uri); + HttpResponse::new().with_status(HttpStatusCode::BadRequest) + }, + } } } -impl HttpHandler for KeyServerHttpHandler { - fn handle(&self, mut req: HttpRequest, mut res: HttpResponse) { - if req.headers.has::() { - warn!(target: "secretstore", "Ignoring {}-request {} with Origin header", req.method, req.uri); - *res.status_mut() = HttpStatusCode::NotFound; - return; - } +impl Service for KeyServerHttpHandler { + type Request = HttpRequest; + type Response = HttpResponse; + type Error = hyper::Error; + type Future = Box + Send>; - let mut req_body = Default::default(); - if let Err(error) = req.read_to_string(&mut req_body) { - warn!(target: "secretstore", "Error {} reading body of {}-request {}", error, req.method, req.uri); - *res.status_mut() = HttpStatusCode::BadRequest; - return; + fn call(&self, req: HttpRequest) -> Self::Future { + if req.headers().has::() { + warn!(target: "secretstore", "Ignoring {}-request {} with Origin header", req.method(), req.uri()); + return Box::new(future::ok(HttpResponse::new().with_status(HttpStatusCode::NotFound))); } - let req_method = req.method.clone(); - let req_uri = req.uri.clone(); - match &req_uri { - &RequestUri::AbsolutePath(ref path) => match parse_request(&req_method, &path, &req_body) { - Request::GenerateServerKey(document, signature, threshold) => { - return_server_public_key(req, res, self.handler.key_server.generate_key(&document, &signature, threshold) - .map_err(|err| { - warn!(target: "secretstore", "GenerateServerKey request {} has failed with: {}", req_uri, err); - err - })); - }, - Request::StoreDocumentKey(document, signature, common_point, encrypted_document_key) => { - return_empty(req, res, self.handler.key_server.store_document_key(&document, &signature, common_point, encrypted_document_key) - .map_err(|err| { - warn!(target: "secretstore", "StoreDocumentKey request {} has failed with: {}", req_uri, err); - err - })); - }, - Request::GenerateDocumentKey(document, signature, threshold) => { - return_document_key(req, res, self.handler.key_server.generate_document_key(&document, &signature, threshold) - .map_err(|err| { - warn!(target: "secretstore", "GenerateDocumentKey request {} has failed with: {}", req_uri, err); - err - })); - }, - Request::GetDocumentKey(document, signature) => { - return_document_key(req, res, self.handler.key_server.restore_document_key(&document, &signature) - .map_err(|err| { - warn!(target: "secretstore", "GetDocumentKey request {} has failed with: {}", req_uri, err); - err - })); - }, - Request::GetDocumentKeyShadow(document, signature) => { - return_document_key_shadow(req, res, self.handler.key_server.restore_document_key_shadow(&document, &signature) - .map_err(|err| { - warn!(target: "secretstore", "GetDocumentKeyShadow request {} has failed with: {}", req_uri, err); - err - })); - }, - Request::SchnorrSignMessage(document, signature, message_hash) => { - return_message_signature(req, res, self.handler.key_server.sign_message_schnorr(&document, &signature, message_hash) - .map_err(|err| { - warn!(target: "secretstore", "SchnorrSignMessage request {} has failed with: {}", req_uri, err); - err - })); - }, - Request::EcdsaSignMessage(document, signature, message_hash) => { - return_message_signature(req, res, self.handler.key_server.sign_message_ecdsa(&document, &signature, message_hash) - .map_err(|err| { - warn!(target: "secretstore", "EcdsaSignMessage request {} has failed with: {}", req_uri, err); - err - })); - }, - Request::ChangeServersSet(old_set_signature, new_set_signature, new_servers_set) => { - return_empty(req, res, self.handler.key_server.change_servers_set(old_set_signature, new_set_signature, new_servers_set) - .map_err(|err| { - warn!(target: "secretstore", "ChangeServersSet request {} has failed with: {}", req_uri, err); - err - })); - }, - Request::Invalid => { - warn!(target: "secretstore", "Ignoring invalid {}-request {}", req_method, req_uri); - *res.status_mut() = HttpStatusCode::BadRequest; - }, - }, - _ => { + let req_method = req.method().clone(); + let req_uri = req.uri().clone(); + // We cannot consume Self because of the Service trait requirement. + let this = self.clone(); + + Box::new(req.body().concat2().map(move |body| { + let path = req_uri.path().to_string(); + if path.starts_with("/") { + this.process(req_method, req_uri, &path, &body) + } else { warn!(target: "secretstore", "Ignoring invalid {}-request {}", req_method, req_uri); - *res.status_mut() = HttpStatusCode::NotFound; - }, - }; + HttpResponse::new().with_status(HttpStatusCode::NotFound) + } + })) } } -fn return_empty(req: HttpRequest, res: HttpResponse, empty: Result<(), Error>) { - return_bytes::(req, res, empty.map(|_| None)) +fn return_empty(req_uri: &Uri, empty: Result<(), Error>) -> HttpResponse { + return_bytes::(req_uri, empty.map(|_| None)) } -fn return_server_public_key(req: HttpRequest, res: HttpResponse, server_public: Result) { - return_bytes(req, res, server_public.map(|k| Some(SerializablePublic(k)))) +fn return_server_public_key(req_uri: &Uri, server_public: Result) -> HttpResponse { + return_bytes(req_uri, server_public.map(|k| Some(SerializablePublic(k)))) } -fn return_message_signature(req: HttpRequest, res: HttpResponse, signature: Result) { - return_bytes(req, res, signature.map(|s| Some(SerializableBytes(s)))) +fn return_message_signature(req_uri: &Uri, signature: Result) -> HttpResponse { + return_bytes(req_uri, signature.map(|s| Some(SerializableBytes(s)))) } -fn return_document_key(req: HttpRequest, res: HttpResponse, document_key: Result) { - return_bytes(req, res, document_key.map(|k| Some(SerializableBytes(k)))) +fn return_document_key(req_uri: &Uri, document_key: Result) -> HttpResponse { + return_bytes(req_uri, document_key.map(|k| Some(SerializableBytes(k)))) } -fn return_document_key_shadow(req: HttpRequest, res: HttpResponse, document_key_shadow: Result) { - return_bytes(req, res, document_key_shadow.map(|k| Some(SerializableEncryptedDocumentKeyShadow { +fn return_document_key_shadow(req_uri: &Uri, document_key_shadow: Result) -> HttpResponse { + return_bytes(req_uri, document_key_shadow.map(|k| Some(SerializableEncryptedDocumentKeyShadow { decrypted_secret: k.decrypted_secret.into(), common_point: k.common_point.expect("always filled when requesting document_key_shadow; qed").into(), decrypt_shadows: k.decrypt_shadows.expect("always filled when requesting document_key_shadow; qed").into_iter().map(Into::into).collect(), }))) } -fn return_bytes(req: HttpRequest, mut res: HttpResponse, result: Result, Error>) { +fn return_bytes(req_uri: &Uri, result: Result, Error>) -> HttpResponse { match result { Ok(Some(result)) => match serde_json::to_vec(&result) { - Ok(result) => { - res.headers_mut().set(header::ContentType::json()); - if let Err(err) = res.send(&result) { - // nothing to do, but to log an error - warn!(target: "secretstore", "response to request {} has failed with: {}", req.uri, err); - } - }, + Ok(result) => HttpResponse::new() + .with_header(header::ContentType::json()) + .with_body(result), Err(err) => { - warn!(target: "secretstore", "response to request {} has failed with: {}", req.uri, err); + warn!(target: "secretstore", "response to request {} has failed with: {}", req_uri, err); + HttpResponse::new().with_status(HttpStatusCode::InternalServerError) } }, - Ok(None) => *res.status_mut() = HttpStatusCode::Ok, - Err(err) => return_error(res, err), + Ok(None) => HttpResponse::new().with_status(HttpStatusCode::Ok), + Err(err) => return_error(err), } } -fn return_error(mut res: HttpResponse, err: Error) { - match err { - Error::BadSignature => *res.status_mut() = HttpStatusCode::BadRequest, - Error::AccessDenied => *res.status_mut() = HttpStatusCode::Forbidden, - Error::DocumentNotFound => *res.status_mut() = HttpStatusCode::NotFound, - Error::Hyper(_) => *res.status_mut() = HttpStatusCode::BadRequest, - Error::Serde(_) => *res.status_mut() = HttpStatusCode::BadRequest, - Error::Database(_) => *res.status_mut() = HttpStatusCode::InternalServerError, - Error::Internal(_) => *res.status_mut() = HttpStatusCode::InternalServerError, - } +fn return_error(err: Error) -> HttpResponse { + let mut res = HttpResponse::new().with_status(match err { + Error::AccessDenied | Error::ConsensusUnreachable | Error::ConsensusTemporaryUnreachable => + HttpStatusCode::Forbidden, + Error::ServerKeyIsNotFound | Error::DocumentKeyIsNotFound => + HttpStatusCode::NotFound, + Error::InsufficientRequesterData(_) | Error::Hyper(_) | Error::Serde(_) + | Error::DocumentKeyAlreadyStored | Error::ServerKeyAlreadyGenerated => + HttpStatusCode::BadRequest, + _ => HttpStatusCode::InternalServerError, + }); // return error text. ignore errors when returning error let error_text = format!("\"{}\"", err); if let Ok(error_text) = serde_json::to_vec(&error_text) { res.headers_mut().set(header::ContentType::json()); - let _ = res.send(&error_text); + res.set_body(error_text); } + + res } -fn parse_request(method: &HttpMethod, uri_path: &str, body: &str) -> Request { +fn parse_request(method: &HttpMethod, uri_path: &str, body: &[u8]) -> Request { let uri_path = match percent_decode(uri_path.as_bytes()).decode_utf8() { Ok(path) => path, Err(_) => return Request::Invalid, @@ -312,7 +346,7 @@ fn parse_request(method: &HttpMethod, uri_path: &str, body: &str) -> Request { } } -fn parse_admin_request(method: &HttpMethod, path: Vec, body: &str) -> Request { +fn parse_admin_request(method: &HttpMethod, path: Vec, body: &[u8]) -> Request { let args_count = path.len(); if *method != HttpMethod::Post || args_count != 4 || path[1] != "servers_set_change" { return Request::Invalid; @@ -328,7 +362,7 @@ fn parse_admin_request(method: &HttpMethod, path: Vec, body: &str) -> Re _ => return Request::Invalid, }; - let new_servers_set: BTreeSet = match serde_json::from_str(body) { + let new_servers_set: BTreeSet = match serde_json::from_slice(body) { Ok(new_servers_set) => new_servers_set, _ => return Request::Invalid, }; @@ -340,20 +374,21 @@ fn parse_admin_request(method: &HttpMethod, path: Vec, body: &str) -> Re #[cfg(test)] mod tests { use std::sync::Arc; - use hyper::method::Method as HttpMethod; + use hyper::Method as HttpMethod; use ethkey::Public; + use traits::KeyServer; use key_server::tests::DummyKeyServer; - use types::all::NodeAddress; + use types::NodeAddress; use super::{parse_request, Request, KeyServerHttpListener}; #[test] fn http_listener_successfully_drops() { - let key_server = Arc::new(DummyKeyServer::default()); + let key_server: Arc = Arc::new(DummyKeyServer::default()); let address = NodeAddress { address: "127.0.0.1".into(), port: 9000 }; - let listener = KeyServerHttpListener::start(address, key_server).unwrap(); + let listener = KeyServerHttpListener::start(address, Arc::downgrade(&key_server)).unwrap(); drop(listener); } - + #[test] fn parse_request_successful() { // POST /shadow/{server_key_id}/{signature}/{threshold} => generate server key @@ -399,7 +434,7 @@ mod tests { let nodes = vec![node1, node2].into_iter().collect(); assert_eq!(parse_request(&HttpMethod::Post, "/admin/servers_set_change/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/b199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01", &r#"["0x843645726384530ffb0c52f175278143b5a93959af7864460f5a4fec9afd1450cfb8aef63dec90657f43f55b13e0a73c7524d4e9a13c051b4e5f1e53f39ecd91", - "0x07230e34ebfe41337d3ed53b186b3861751f2401ee74b988bba55694e2a6f60c757677e194be2e53c3523cc8548694e636e6acb35c4e8fdc5e29d28679b9b2f3"]"#), + "0x07230e34ebfe41337d3ed53b186b3861751f2401ee74b988bba55694e2a6f60c757677e194be2e53c3523cc8548694e636e6acb35c4e8fdc5e29d28679b9b2f3"]"#.as_bytes()), Request::ChangeServersSet( "a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap(), "b199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap(), @@ -420,9 +455,9 @@ mod tests { assert_eq!(parse_request(&HttpMethod::Get, "/ecdsa/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/0000000000000000000000000000000000000000000000000000000000000002/0000000000000000000000000000000000000000000000000000000000000002", Default::default()), Request::Invalid); assert_eq!(parse_request(&HttpMethod::Post, "/admin/servers_set_change/xxx/yyy", &r#"["0x843645726384530ffb0c52f175278143b5a93959af7864460f5a4fec9afd1450cfb8aef63dec90657f43f55b13e0a73c7524d4e9a13c051b4e5f1e53f39ecd91", - "0x07230e34ebfe41337d3ed53b186b3861751f2401ee74b988bba55694e2a6f60c757677e194be2e53c3523cc8548694e636e6acb35c4e8fdc5e29d28679b9b2f3"]"#), + "0x07230e34ebfe41337d3ed53b186b3861751f2401ee74b988bba55694e2a6f60c757677e194be2e53c3523cc8548694e636e6acb35c4e8fdc5e29d28679b9b2f3"]"#.as_bytes()), Request::Invalid); - assert_eq!(parse_request(&HttpMethod::Post, "/admin/servers_set_change/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01", ""), + assert_eq!(parse_request(&HttpMethod::Post, "/admin/servers_set_change/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01", "".as_bytes()), Request::Invalid); } } diff --git a/secret_store/src/listener/mod.rs b/secret_store/src/listener/mod.rs index df96c583d44a979df338fe191c2e3aad2e253131..8837e7ffd634c172179bead900593f59b8e4c9ff 100644 --- a/secret_store/src/listener/mod.rs +++ b/secret_store/src/listener/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -16,22 +16,50 @@ pub mod http_listener; pub mod service_contract; +pub mod service_contract_aggregate; pub mod service_contract_listener; mod tasks_queue; use std::collections::BTreeSet; use std::sync::Arc; use traits::{ServerKeyGenerator, DocumentKeyServer, MessageSigner, AdminSessionsServer, KeyServer}; -use types::all::{Error, Public, MessageHash, EncryptedMessageSignature, RequestSignature, ServerKeyId, - EncryptedDocumentKey, EncryptedDocumentKeyShadow, NodeId}; +use types::{Error, Public, MessageHash, EncryptedMessageSignature, RequestSignature, ServerKeyId, + EncryptedDocumentKey, EncryptedDocumentKeyShadow, NodeId, Requester}; + +/// Available API mask. +#[derive(Debug, Default)] +pub struct ApiMask { + /// Accept server key generation requests. + pub server_key_generation_requests: bool, + /// Accept server key retrieval requests. + pub server_key_retrieval_requests: bool, + /// Accept document key store requests. + pub document_key_store_requests: bool, + /// Accept document key shadow retrieval requests. + pub document_key_shadow_retrieval_requests: bool, +} +/// Combined HTTP + service contract listener. pub struct Listener { key_server: Arc, _http: Option, _contract: Option>, } +impl ApiMask { + /// Create mask that accepts all requests. + pub fn all() -> Self { + ApiMask { + server_key_generation_requests: true, + server_key_retrieval_requests: true, + document_key_store_requests: true, + document_key_shadow_retrieval_requests: true, + } + } +} + impl Listener { + /// Create new listener. pub fn new(key_server: Arc, http: Option, contract: Option>) -> Self { Self { key_server: key_server, @@ -44,36 +72,36 @@ impl Listener { impl KeyServer for Listener {} impl ServerKeyGenerator for Listener { - fn generate_key(&self, key_id: &ServerKeyId, signature: &RequestSignature, threshold: usize) -> Result { - self.key_server.generate_key(key_id, signature, threshold) + fn generate_key(&self, key_id: &ServerKeyId, author: &Requester, threshold: usize) -> Result { + self.key_server.generate_key(key_id, author, threshold) } } impl DocumentKeyServer for Listener { - fn store_document_key(&self, key_id: &ServerKeyId, signature: &RequestSignature, common_point: Public, encrypted_document_key: Public) -> Result<(), Error> { - self.key_server.store_document_key(key_id, signature, common_point, encrypted_document_key) + fn store_document_key(&self, key_id: &ServerKeyId, author: &Requester, common_point: Public, encrypted_document_key: Public) -> Result<(), Error> { + self.key_server.store_document_key(key_id, author, common_point, encrypted_document_key) } - fn generate_document_key(&self, key_id: &ServerKeyId, signature: &RequestSignature, threshold: usize) -> Result { - self.key_server.generate_document_key(key_id, signature, threshold) + fn generate_document_key(&self, key_id: &ServerKeyId, author: &Requester, threshold: usize) -> Result { + self.key_server.generate_document_key(key_id, author, threshold) } - fn restore_document_key(&self, key_id: &ServerKeyId, signature: &RequestSignature) -> Result { - self.key_server.restore_document_key(key_id, signature) + fn restore_document_key(&self, key_id: &ServerKeyId, requester: &Requester) -> Result { + self.key_server.restore_document_key(key_id, requester) } - fn restore_document_key_shadow(&self, key_id: &ServerKeyId, signature: &RequestSignature) -> Result { - self.key_server.restore_document_key_shadow(key_id, signature) + fn restore_document_key_shadow(&self, key_id: &ServerKeyId, requester: &Requester) -> Result { + self.key_server.restore_document_key_shadow(key_id, requester) } } impl MessageSigner for Listener { - fn sign_message_schnorr(&self, key_id: &ServerKeyId, signature: &RequestSignature, message: MessageHash) -> Result { - self.key_server.sign_message_schnorr(key_id, signature, message) + fn sign_message_schnorr(&self, key_id: &ServerKeyId, requester: &Requester, message: MessageHash) -> Result { + self.key_server.sign_message_schnorr(key_id, requester, message) } - fn sign_message_ecdsa(&self, key_id: &ServerKeyId, signature: &RequestSignature, message: MessageHash) -> Result { - self.key_server.sign_message_ecdsa(key_id, signature, message) + fn sign_message_ecdsa(&self, key_id: &ServerKeyId, requester: &Requester, message: MessageHash) -> Result { + self.key_server.sign_message_ecdsa(key_id, requester, message) } } @@ -81,4 +109,4 @@ impl AdminSessionsServer for Listener { fn change_servers_set(&self, old_set_signature: RequestSignature, new_set_signature: RequestSignature, new_servers_set: BTreeSet) -> Result<(), Error> { self.key_server.change_servers_set(old_set_signature, new_set_signature, new_servers_set) } -} \ No newline at end of file +} diff --git a/secret_store/src/listener/service_contract.rs b/secret_store/src/listener/service_contract.rs index f2d13f192f75ddfcb17e6a46484083c87af72580..daf70cd6488365bdaf2ea7d75c5f253d9ed289c9 100644 --- a/secret_store/src/listener/service_contract.rs +++ b/secret_store/src/listener/service_contract.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -16,28 +16,49 @@ use std::sync::Arc; use parking_lot::RwLock; +use ethabi::RawLog; use ethcore::filter::Filter; -use ethcore::client::{Client, BlockChainClient, BlockId, RegistryInfo, CallContract}; -use ethkey::{Public, Signature, public_to_address}; +use ethcore::client::{Client, BlockChainClient, BlockId, CallContract}; +use ethkey::{Public, public_to_address}; use hash::keccak; +use bytes::Bytes; use ethereum_types::{H256, U256, Address}; +use listener::ApiMask; use listener::service_contract_listener::ServiceTask; use trusted_client::TrustedClient; +use helpers::{get_confirmed_block_hash, REQUEST_CONFIRMATIONS_REQUIRED}; use {ServerKeyId, NodeKeyPair, ContractAddress}; use_contract!(service, "Service", "res/service.json"); -/// Name of the SecretStore contract in the registry. -const SERVICE_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_service"; - -/// Key server has been added to the set. -const SERVER_KEY_REQUESTED_EVENT_NAME: &'static [u8] = &*b"ServerKeyRequested(bytes32,uint256)"; - -/// Number of confirmations required before request can be processed. -const REQUEST_CONFIRMATIONS_REQUIRED: u64 = 3; +/// Name of the general SecretStore contract in the registry. +pub const SERVICE_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_service"; +/// Name of the server key generation SecretStore contract in the registry. +pub const SRV_KEY_GEN_SERVICE_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_service_srv_gen"; +/// Name of the server key retrieval SecretStore contract in the registry. +pub const SRV_KEY_RETR_SERVICE_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_service_srv_retr"; +/// Name of the document key store SecretStore contract in the registry. +pub const DOC_KEY_STORE_SERVICE_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_service_doc_store"; +/// Name of the document key retrieval SecretStore contract in the registry. +pub const DOC_KEY_SRETR_SERVICE_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_service_doc_sretr"; + +/// Server key generation has been requested. +const SERVER_KEY_GENERATION_REQUESTED_EVENT_NAME: &'static [u8] = &*b"ServerKeyGenerationRequested(bytes32,address,uint8)"; +/// Server key retrieval has been requested. +const SERVER_KEY_RETRIEVAL_REQUESTED_EVENT_NAME: &'static [u8] = &*b"ServerKeyRetrievalRequested(bytes32)"; +/// Document key store has been requested. +const DOCUMENT_KEY_STORE_REQUESTED_EVENT_NAME: &'static [u8] = &*b"DocumentKeyStoreRequested(bytes32,address,bytes,bytes)"; +/// Document key common part retrieval has been requested. +const DOCUMENT_KEY_COMMON_PART_RETRIEVAL_REQUESTED_EVENT_NAME: &'static [u8] = &*b"DocumentKeyCommonRetrievalRequested(bytes32,address)"; +/// Document key personal part retrieval has been requested. +const DOCUMENT_KEY_PERSONAL_PART_RETRIEVAL_REQUESTED_EVENT_NAME: &'static [u8] = &*b"DocumentKeyPersonalRetrievalRequested(bytes32,bytes)"; lazy_static! { - static ref SERVER_KEY_REQUESTED_EVENT_NAME_HASH: H256 = keccak(SERVER_KEY_REQUESTED_EVENT_NAME); + pub static ref SERVER_KEY_GENERATION_REQUESTED_EVENT_NAME_HASH: H256 = keccak(SERVER_KEY_GENERATION_REQUESTED_EVENT_NAME); + pub static ref SERVER_KEY_RETRIEVAL_REQUESTED_EVENT_NAME_HASH: H256 = keccak(SERVER_KEY_RETRIEVAL_REQUESTED_EVENT_NAME); + pub static ref DOCUMENT_KEY_STORE_REQUESTED_EVENT_NAME_HASH: H256 = keccak(DOCUMENT_KEY_STORE_REQUESTED_EVENT_NAME); + pub static ref DOCUMENT_KEY_COMMON_PART_RETRIEVAL_REQUESTED_EVENT_NAME_HASH: H256 = keccak(DOCUMENT_KEY_COMMON_PART_RETRIEVAL_REQUESTED_EVENT_NAME); + pub static ref DOCUMENT_KEY_PERSONAL_PART_RETRIEVAL_REQUESTED_EVENT_NAME_HASH: H256 = keccak(DOCUMENT_KEY_PERSONAL_PART_RETRIEVAL_REQUESTED_EVENT_NAME); } /// Service contract trait. @@ -45,105 +66,181 @@ pub trait ServiceContract: Send + Sync { /// Update contract when new blocks are enacted. Returns true if contract is installed && up-to-date (i.e. chain is synced). fn update(&self) -> bool; /// Read recent contract logs. Returns topics of every entry. - fn read_logs(&self) -> Box>>; + fn read_logs(&self) -> Box>; /// Publish generated key. fn read_pending_requests(&self) -> Box>; - /// Publish server key. - fn publish_server_key(&self, server_key_id: &ServerKeyId, server_key: &Public) -> Result<(), String>; + /// Publish generated server key. + fn publish_generated_server_key(&self, origin: &Address, server_key_id: &ServerKeyId, server_key: Public) -> Result<(), String>; + /// Publish server key generation error. + fn publish_server_key_generation_error(&self, origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String>; + /// Publish retrieved server key. + fn publish_retrieved_server_key(&self, origin: &Address, server_key_id: &ServerKeyId, server_key: Public, threshold: usize) -> Result<(), String>; + /// Publish server key retrieval error. + fn publish_server_key_retrieval_error(&self, origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String>; + /// Publish stored document key. + fn publish_stored_document_key(&self, origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String>; + /// Publish document key store error. + fn publish_document_key_store_error(&self, origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String>; + /// Publish retrieved document key common. + fn publish_retrieved_document_key_common(&self, origin: &Address, server_key_id: &ServerKeyId, requester: &Address, common_point: Public, threshold: usize) -> Result<(), String>; + /// Publish retrieved document key personal. + fn publish_retrieved_document_key_personal(&self, origin: &Address, server_key_id: &ServerKeyId, requester: &Address, participants: &[Address], decrypted_secret: Public, shadow: Bytes) -> Result<(), String>; + /// Publish document key store error. + fn publish_document_key_retrieval_error(&self, origin: &Address, server_key_id: &ServerKeyId, requester: &Address) -> Result<(), String>; } /// On-chain service contract. pub struct OnChainServiceContract { + /// Requests mask. + mask: ApiMask, /// Blockchain client. client: TrustedClient, /// This node key pair. self_key_pair: Arc, - /// Contract addresss. - address: ContractAddress, + /// Contract registry name (if any). + name: String, + /// Contract address source. + address_source: ContractAddress, + /// Contract. + contract: service::Service, /// Contract. data: RwLock, } /// On-chain service contract data. struct ServiceData { - /// Contract. - pub contract: service::Service, - /// Contract address. - pub contract_address: Address, + /// Current contract address. + pub contract_address: Option
, /// Last block we have read logs from. pub last_log_block: Option, } /// Pending requests iterator. -struct PendingRequestsIterator { - /// Blockchain client. - client: Arc, - /// Contract. - contract: service::Service, - /// Contract address. - contract_address: Address, - /// This node key pair. - self_key_pair: Arc, - /// Block, this iterator is created for. - block: H256, +struct PendingRequestsIterator Option<(bool, ServiceTask)>> { + /// Pending request read function. + read_request: F, /// Current request index. index: U256, /// Requests length. length: U256, } +/// Server key generation related functions. +struct ServerKeyGenerationService; +/// Server key retrieval related functions. +struct ServerKeyRetrievalService; +/// Document key store related functions. +struct DocumentKeyStoreService; +/// Document key shadow retrievalrelated functions. +struct DocumentKeyShadowRetrievalService; + impl OnChainServiceContract { /// Create new on-chain service contract. - pub fn new(client: TrustedClient, address: ContractAddress, self_key_pair: Arc) -> Self { - let contract_addr = match address { - ContractAddress::Registry => client.get().and_then(|c| c.registry_address(SERVICE_CONTRACT_REGISTRY_NAME.to_owned(), BlockId::Latest) - .map(|address| { - trace!(target: "secretstore", "{}: installing service contract from address {}", - self_key_pair.public(), address); - address - })) - .unwrap_or_default(), - ContractAddress::Address(ref address) => { - trace!(target: "secretstore", "{}: installing service contract from address {}", - self_key_pair.public(), address); - address.clone() - }, - }; - - OnChainServiceContract { + pub fn new(mask: ApiMask, client: TrustedClient, name: String, address_source: ContractAddress, self_key_pair: Arc) -> Self { + let contract = OnChainServiceContract { + mask: mask, client: client, self_key_pair: self_key_pair, - address: address, + name: name, + address_source: address_source, + contract: service::Service::default(), data: RwLock::new(ServiceData { - contract: service::Service::default(), - contract_address: contract_addr, + contract_address: None, last_log_block: None, }), + }; + + contract.update_contract_address(); + contract + } + + /// Send transaction to the service contract. + fn send_contract_transaction(&self, tx_name: &str, origin: &Address, server_key_id: &ServerKeyId, is_response_required: C, prepare_tx: P) -> Result<(), String> + where C: FnOnce(&Client, &Address, &service::Service, &ServerKeyId, &Address) -> bool, + P: FnOnce(&Client, &Address, &service::Service) -> Result { + // only publish if contract address is set && client is online + let client = match self.client.get() { + Some(client) => client, + None => return Err("trusted client is required to publish key".into()), + }; + + // only publish key if contract waits for publication + // failing is ok here - it could be that enough confirmations have been recevied + // or key has been requested using HTTP API + let self_address = public_to_address(self.self_key_pair.public()); + if !is_response_required(&*client, origin, &self.contract, server_key_id, &self_address) { + return Ok(()); } + + // prepare transaction data + let transaction_data = prepare_tx(&*client, origin, &self.contract)?; + + // send transaction + self.client.transact_contract( + origin.clone(), + transaction_data + ).map_err(|e| format!("{}", e))?; + + trace!(target: "secretstore", "{}: transaction {} sent to service contract", + self.self_key_pair.public(), tx_name); + + Ok(()) + } + + /// Create task-specific pending requests iterator. + fn create_pending_requests_iterator< + C: 'static + Fn(&Client, &Address, &service::Service, &BlockId) -> Result, + R: 'static + Fn(&NodeKeyPair, &Client, &Address, &service::Service, &BlockId, U256) -> Result<(bool, ServiceTask), String> + >(&self, client: Arc, contract_address: &Address, block: &BlockId, get_count: C, read_item: R) -> Box> { + let contract = service::Service::default(); + get_count(&*client, contract_address, &contract, block) + .map(|count| { + let client = client.clone(); + let self_key_pair = self.self_key_pair.clone(); + let contract_address = contract_address.clone(); + let block = block.clone(); + Box::new(PendingRequestsIterator { + read_request: move |index| read_item(&*self_key_pair, &*client, &contract_address, &contract, &block, index) + .map_err(|error| { + warn!(target: "secretstore", "{}: reading pending request failed: {}", + self_key_pair.public(), error); + error + }) + .ok(), + index: 0.into(), + length: count, + }) as Box> + }) + .map_err(|error| { + warn!(target: "secretstore", "{}: creating pending requests iterator failed: {}", + self.self_key_pair.public(), error); + error + }) + .ok() + .unwrap_or_else(|| Box::new(::std::iter::empty())) + } + + /// Update service contract address. + fn update_contract_address(&self) -> bool { + let contract_address = self.client.read_contract_address(self.name.clone(), &self.address_source); + let mut data = self.data.write(); + if contract_address != data.contract_address { + trace!(target: "secretstore", "{}: installing {} service contract from address {:?}", + self.self_key_pair.public(), self.name, contract_address); + + data.contract_address = contract_address; + } + + data.contract_address.is_some() } } impl ServiceContract for OnChainServiceContract { fn update(&self) -> bool { - // TODO [Sec]: registry_address currently reads from BlockId::Latest, instead of - // from block with REQUEST_CONFIRMATIONS_REQUIRED confirmations - if let &ContractAddress::Registry = &self.address { - if let Some(client) = self.client.get() { - // update contract address from registry - let service_contract_addr = client.registry_address(SERVICE_CONTRACT_REGISTRY_NAME.to_owned(), BlockId::Latest).unwrap_or_default(); - if self.data.read().contract_address != service_contract_addr { - trace!(target: "secretstore", "{}: installing service contract from address {}", - self.self_key_pair.public(), service_contract_addr); - self.data.write().contract_address = service_contract_addr; - } - } - } - - self.data.read().contract_address != Default::default() - && self.client.get().is_some() + self.update_contract_address() && self.client.get().is_some() } - fn read_logs(&self) -> Box>> { + fn read_logs(&self) -> Box> { let client = match self.client.get() { Some(client) => client, None => { @@ -156,7 +253,10 @@ impl ServiceContract for OnChainServiceContract { // prepare range of blocks to read logs from let (address, first_block, last_block) = { let mut data = self.data.write(); - let address = data.contract_address; + let address = match data.contract_address { + Some(address) => address, + None => return Box::new(::std::iter::empty()), // no contract installed + }; let confirmed_block = match get_confirmed_block_hash(&*client, REQUEST_CONFIRMATIONS_REQUIRED) { Some(confirmed_block) => confirmed_block, None => return Box::new(::std::iter::empty()), // no block with enough confirmations @@ -181,16 +281,33 @@ impl ServiceContract for OnChainServiceContract { from_block: BlockId::Hash(first_block), to_block: BlockId::Hash(last_block), address: Some(vec![address]), - topics: vec![ - Some(vec![*SERVER_KEY_REQUESTED_EVENT_NAME_HASH]), - None, - None, - None, - ], + topics: vec![Some(mask_topics(&self.mask))], limit: None, }); - Box::new(request_logs.into_iter().map(|log| log.entry.topics)) + Box::new(request_logs.into_iter() + .filter_map(|log| { + let raw_log: RawLog = (log.entry.topics.into_iter().map(|t| t.0.into()).collect(), log.entry.data).into(); + if raw_log.topics[0] == *SERVER_KEY_GENERATION_REQUESTED_EVENT_NAME_HASH { + ServerKeyGenerationService::parse_log(&address, &self.contract, raw_log) + } else if raw_log.topics[0] == *SERVER_KEY_RETRIEVAL_REQUESTED_EVENT_NAME_HASH { + ServerKeyRetrievalService::parse_log(&address, &self.contract, raw_log) + } else if raw_log.topics[0] == *DOCUMENT_KEY_STORE_REQUESTED_EVENT_NAME_HASH { + DocumentKeyStoreService::parse_log(&address, &self.contract, raw_log) + } else if raw_log.topics[0] == *DOCUMENT_KEY_COMMON_PART_RETRIEVAL_REQUESTED_EVENT_NAME_HASH { + DocumentKeyShadowRetrievalService::parse_common_request_log(&address, &self.contract, raw_log) + } else if raw_log.topics[0] == *DOCUMENT_KEY_PERSONAL_PART_RETRIEVAL_REQUESTED_EVENT_NAME_HASH { + DocumentKeyShadowRetrievalService::parse_personal_request_log(&address, &self.contract, raw_log) + } else { + Err("unknown type of log entry".into()) + } + .map_err(|error| { + warn!(target: "secretstore", "{}: error parsing log entry from service contract: {}", + self.self_key_pair.public(), error); + error + }) + .ok() + }).collect::>().into_iter()) } fn read_pending_requests(&self) -> Box> { @@ -202,84 +319,101 @@ impl ServiceContract for OnChainServiceContract { // we only need requests that are here for more than REQUEST_CONFIRMATIONS_REQUIRED blocks // => we're reading from Latest - (REQUEST_CONFIRMATIONS_REQUIRED + 1) block let data = self.data.read(); - match data.contract_address == Default::default() { - true => Box::new(::std::iter::empty()), - false => get_confirmed_block_hash(&*client, REQUEST_CONFIRMATIONS_REQUIRED + 1) - .and_then(|b| { - let contract_address = data.contract_address; - let do_call = |data| client.call_contract(BlockId::Hash(b), contract_address, data); - data.contract.functions().server_key_generation_requests_count().call(&do_call) - .map_err(|error| { - warn!(target: "secretstore", "{}: call to server_key_generation_requests_count failed: {}", - self.self_key_pair.public(), error); - error - }) - .map(|l| (b, l)) - .ok() + match data.contract_address { + None => Box::new(::std::iter::empty()), + Some(contract_address) => get_confirmed_block_hash(&*client, REQUEST_CONFIRMATIONS_REQUIRED + 1) + .map(|b| { + let block = BlockId::Hash(b); + let iter = match self.mask.server_key_generation_requests { + true => Box::new(self.create_pending_requests_iterator(client.clone(), &contract_address, &block, + &ServerKeyGenerationService::read_pending_requests_count, + &ServerKeyGenerationService::read_pending_request)) as Box>, + false => Box::new(::std::iter::empty()), + }; + let iter = match self.mask.server_key_retrieval_requests { + true => Box::new(iter.chain(self.create_pending_requests_iterator(client.clone(), &contract_address, &block, + &ServerKeyRetrievalService::read_pending_requests_count, + &ServerKeyRetrievalService::read_pending_request))), + false => iter, + }; + let iter = match self.mask.document_key_store_requests { + true => Box::new(iter.chain(self.create_pending_requests_iterator(client.clone(), &contract_address, &block, + &DocumentKeyStoreService::read_pending_requests_count, + &DocumentKeyStoreService::read_pending_request))), + false => iter, + }; + let iter = match self.mask.document_key_shadow_retrieval_requests { + true => Box::new(iter.chain(self.create_pending_requests_iterator(client, &contract_address, &block, + &DocumentKeyShadowRetrievalService::read_pending_requests_count, + &DocumentKeyShadowRetrievalService::read_pending_request))), + false => iter + }; + + iter }) - .map(|(b, l)| Box::new(PendingRequestsIterator { - client: client, - contract: service::Service::default(), - contract_address: data.contract_address, - self_key_pair: self.self_key_pair.clone(), - block: b, - index: 0.into(), - length: l, - }) as Box>) .unwrap_or_else(|| Box::new(::std::iter::empty())) } } - fn publish_server_key(&self, server_key_id: &ServerKeyId, server_key: &Public) -> Result<(), String> { - // only publish if contract address is set && client is online - let data = self.data.read(); - if data.contract_address == Default::default() { - // it is not an error, because key could be generated even without contract - return Ok(()); - } + fn publish_generated_server_key(&self, origin: &Address, server_key_id: &ServerKeyId, server_key: Public) -> Result<(), String> { + self.send_contract_transaction("publish_generated_server_key", origin, server_key_id, ServerKeyGenerationService::is_response_required, + |_, _, service| Ok(ServerKeyGenerationService::prepare_pubish_tx_data(service, server_key_id, &server_key))) + } - let client = match self.client.get() { - Some(client) => client, - None => return Err("trusted client is required to publish key".into()), - }; + fn publish_server_key_generation_error(&self, origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String> { + self.send_contract_transaction("publish_server_key_generation_error", origin, server_key_id, ServerKeyGenerationService::is_response_required, + |_, _, service| Ok(ServerKeyGenerationService::prepare_error_tx_data(service, server_key_id))) + } - // only publish key if contract waits for publication - // failing is ok here - it could be that enough confirmations have been recevied - // or key has been requested using HTTP API - let contract_address = data.contract_address; - let do_call = |data| client.call_contract(BlockId::Latest, contract_address, data); - let self_address = public_to_address(self.self_key_pair.public()); - if data.contract.functions() - .get_server_key_confirmation_status() - .call(*server_key_id, self_address, &do_call) - .unwrap_or(false) { - return Ok(()); - } + fn publish_retrieved_server_key(&self, origin: &Address, server_key_id: &ServerKeyId, server_key: Public, threshold: usize) -> Result<(), String> { + let threshold = serialize_threshold(threshold)?; + self.send_contract_transaction("publish_retrieved_server_key", origin, server_key_id, ServerKeyRetrievalService::is_response_required, + |_, _, service| Ok(ServerKeyRetrievalService::prepare_pubish_tx_data(service, server_key_id, server_key, threshold))) + } - // prepare transaction data - let server_key_hash = keccak(server_key); - let signed_server_key = self.self_key_pair.sign(&server_key_hash).map_err(|e| format!("{}", e))?; - let signed_server_key: Signature = signed_server_key.into_electrum().into(); - let transaction_data = data.contract.functions() - .server_key_generated() - .input(*server_key_id, - server_key.to_vec(), - signed_server_key.v(), - signed_server_key.r(), - signed_server_key.s(), - ); + fn publish_server_key_retrieval_error(&self, origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String> { + self.send_contract_transaction("publish_server_key_retrieval_error", origin, server_key_id, ServerKeyRetrievalService::is_response_required, + |_, _, service| Ok(ServerKeyRetrievalService::prepare_error_tx_data(service, server_key_id))) + } - // send transaction - client.transact_contract( - data.contract_address, - transaction_data - ).map_err(|e| format!("{}", e))?; + fn publish_stored_document_key(&self, origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String> { + self.send_contract_transaction("publish_stored_document_key", origin, server_key_id, DocumentKeyStoreService::is_response_required, + |_, _, service| Ok(DocumentKeyStoreService::prepare_pubish_tx_data(service, server_key_id))) + } - Ok(()) + fn publish_document_key_store_error(&self, origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String> { + self.send_contract_transaction("publish_document_key_store_error", origin, server_key_id, DocumentKeyStoreService::is_response_required, + |_, _, service| Ok(DocumentKeyStoreService::prepare_error_tx_data(service, server_key_id))) + } + + fn publish_retrieved_document_key_common(&self, origin: &Address, server_key_id: &ServerKeyId, requester: &Address, common_point: Public, threshold: usize) -> Result<(), String> { + let threshold = serialize_threshold(threshold)?; + self.send_contract_transaction("publish_retrieved_document_key_common", origin, server_key_id, + |client, contract_address, contract, server_key_id, key_server| + DocumentKeyShadowRetrievalService::is_response_required(client, contract_address, contract, server_key_id, requester, key_server), + |_, _, service| + Ok(DocumentKeyShadowRetrievalService::prepare_pubish_common_tx_data(service, server_key_id, requester, common_point, threshold)) + ) + } + + fn publish_retrieved_document_key_personal(&self, origin: &Address, server_key_id: &ServerKeyId, requester: &Address, participants: &[Address], decrypted_secret: Public, shadow: Bytes) -> Result<(), String> { + self.send_contract_transaction("publish_retrieved_document_key_personal", origin, server_key_id, |_, _, _, _, _| true, + move |client, address, service| + DocumentKeyShadowRetrievalService::prepare_pubish_personal_tx_data(client, address, service, server_key_id, requester, participants, decrypted_secret, shadow) + ) + } + + fn publish_document_key_retrieval_error(&self, origin: &Address, server_key_id: &ServerKeyId, requester: &Address) -> Result<(), String> { + self.send_contract_transaction("publish_document_key_retrieval_error", origin, server_key_id, + |client, contract_address, contract, server_key_id, key_server| + DocumentKeyShadowRetrievalService::is_response_required(client, contract_address, contract, server_key_id, requester, key_server), + |_, _, service| + Ok(DocumentKeyShadowRetrievalService::prepare_error_tx_data(service, server_key_id, requester)) + ) } } -impl Iterator for PendingRequestsIterator { +impl Iterator for PendingRequestsIterator where F: Fn(U256) -> Option<(bool, ServiceTask)> { type Item = (bool, ServiceTask); fn next(&mut self) -> Option<(bool, ServiceTask)> { @@ -290,49 +424,388 @@ impl Iterator for PendingRequestsIterator { let index = self.index.clone(); self.index = self.index + 1.into(); - let self_address = public_to_address(self.self_key_pair.public()); - let contract_address = self.contract_address; - let do_call = |data| self.client.call_contract(BlockId::Hash(self.block.clone()), contract_address, data); - self.contract.functions().get_server_key_id().call(index, &do_call) - .and_then(|server_key_id| - self.contract.functions().get_server_key_threshold().call(server_key_id, &do_call) - .map(|threshold| (server_key_id, threshold))) - .and_then(|(server_key_id, threshold)| - self.contract.functions().get_server_key_confirmation_status().call(server_key_id, self_address, &do_call) - .map(|is_confirmed| (server_key_id, threshold, is_confirmed))) - .map(|(server_key_id, threshold, is_confirmed)| - Some((is_confirmed, ServiceTask::GenerateServerKey(server_key_id, threshold.into())))) - .map_err(|error| { - warn!(target: "secretstore", "{}: reading service contract request failed: {}", - self.self_key_pair.public(), error); - () + (self.read_request)(index) + } +} + +/// Returns vector of logs topics to listen to. +pub fn mask_topics(mask: &ApiMask) -> Vec { + let mut topics = Vec::new(); + if mask.server_key_generation_requests { + topics.push(*SERVER_KEY_GENERATION_REQUESTED_EVENT_NAME_HASH); + } + if mask.server_key_retrieval_requests { + topics.push(*SERVER_KEY_RETRIEVAL_REQUESTED_EVENT_NAME_HASH); + } + if mask.document_key_store_requests { + topics.push(*DOCUMENT_KEY_STORE_REQUESTED_EVENT_NAME_HASH); + } + if mask.document_key_shadow_retrieval_requests { + topics.push(*DOCUMENT_KEY_COMMON_PART_RETRIEVAL_REQUESTED_EVENT_NAME_HASH); + topics.push(*DOCUMENT_KEY_PERSONAL_PART_RETRIEVAL_REQUESTED_EVENT_NAME_HASH); + } + topics +} + +impl ServerKeyGenerationService { + /// Parse request log entry. + pub fn parse_log(origin: &Address, contract: &service::Service, raw_log: RawLog) -> Result { + let event = contract.events().server_key_generation_requested(); + match event.parse_log(raw_log) { + Ok(l) => Ok(ServiceTask::GenerateServerKey(origin.clone(), l.server_key_id, l.author, parse_threshold(l.threshold)?)), + Err(e) => Err(format!("{}", e)), + } + } + + /// Check if response from key server is required. + pub fn is_response_required(client: &Client, contract_address: &Address, contract: &service::Service, server_key_id: &ServerKeyId, key_server: &Address) -> bool { + // we're checking confirmation in Latest block, because we're interested in latest contract state here + let do_call = |data| client.call_contract(BlockId::Latest, *contract_address, data); + contract.functions() + .is_server_key_generation_response_required() + .call(*server_key_id, key_server.clone(), &do_call) + .unwrap_or(true) + } + + /// Prepare publish key transaction data. + pub fn prepare_pubish_tx_data(contract: &service::Service, server_key_id: &ServerKeyId, server_key_public: &Public) -> Bytes { + contract.functions() + .server_key_generated() + .input(*server_key_id, server_key_public.to_vec()) + } + + /// Prepare error transaction data. + pub fn prepare_error_tx_data(contract: &service::Service, server_key_id: &ServerKeyId) -> Bytes { + contract.functions() + .server_key_generation_error() + .input(*server_key_id) + } + + /// Read pending requests count. + fn read_pending_requests_count(client: &Client, contract_address: &Address, _contract: &service::Service, block: &BlockId) -> Result { + let do_call = |data| client.call_contract(block.clone(), contract_address.clone(), data); + let contract = service::Service::default(); + contract.functions() + .server_key_generation_requests_count() + .call(&do_call) + .map_err(|error| format!("{}", error)) + } + + /// Read pending request. + fn read_pending_request(self_key_pair: &NodeKeyPair, client: &Client, contract_address: &Address, contract: &service::Service, block: &BlockId, index: U256) -> Result<(bool, ServiceTask), String> { + let self_address = public_to_address(self_key_pair.public()); + let do_call = |d| client.call_contract(block.clone(), contract_address.clone(), d); + contract.functions() + .get_server_key_generation_request() + .call(index, &do_call) + .map_err(|error| format!("{}", error)) + .and_then(|(server_key_id, author, threshold)| parse_threshold(threshold) + .map(|threshold| (server_key_id, author, threshold))) + .and_then(|(server_key_id, author, threshold)| contract.functions() + .is_server_key_generation_response_required() + .call(server_key_id.clone(), self_address, &do_call) + .map(|not_confirmed| ( + not_confirmed, + ServiceTask::GenerateServerKey( + contract_address.clone(), + server_key_id, + author, + threshold, + ))) + .map_err(|error| format!("{}", error))) + } +} + +impl ServerKeyRetrievalService { + /// Parse request log entry. + pub fn parse_log(origin: &Address, contract: &service::Service, raw_log: RawLog) -> Result { + let event = contract.events().server_key_retrieval_requested(); + match event.parse_log(raw_log) { + Ok(l) => Ok(ServiceTask::RetrieveServerKey(origin.clone(), l.server_key_id)), + Err(e) => Err(format!("{}", e)), + } + } + + /// Check if response from key server is required. + pub fn is_response_required(client: &Client, contract_address: &Address, contract: &service::Service, server_key_id: &ServerKeyId, key_server: &Address) -> bool { + // we're checking confirmation in Latest block, because we're interested in latest contract state here + let do_call = |data| client.call_contract(BlockId::Latest, *contract_address, data); + contract.functions() + .is_server_key_retrieval_response_required() + .call(*server_key_id, key_server.clone(), &do_call) + .unwrap_or(true) + } + + /// Prepare publish key transaction data. + pub fn prepare_pubish_tx_data(contract: &service::Service, server_key_id: &ServerKeyId, server_key_public: Public, threshold: U256) -> Bytes { + contract.functions() + .server_key_retrieved() + .input(*server_key_id, server_key_public.to_vec(), threshold) + } + + /// Prepare error transaction data. + pub fn prepare_error_tx_data(contract: &service::Service, server_key_id: &ServerKeyId) -> Bytes { + contract.functions() + .server_key_retrieval_error() + .input(*server_key_id) + } + + /// Read pending requests count. + fn read_pending_requests_count(client: &Client, contract_address: &Address, _contract: &service::Service, block: &BlockId) -> Result { + let do_call = |data| client.call_contract(block.clone(), contract_address.clone(), data); + let contract = service::Service::default(); + contract.functions() + .server_key_retrieval_requests_count() + .call(&do_call) + .map_err(|error| format!("{}", error)) + } + + /// Read pending request. + fn read_pending_request(self_key_pair: &NodeKeyPair, client: &Client, contract_address: &Address, contract: &service::Service, block: &BlockId, index: U256) -> Result<(bool, ServiceTask), String> { + let self_address = public_to_address(self_key_pair.public()); + let do_call = |d| client.call_contract(block.clone(), contract_address.clone(), d); + contract.functions() + .get_server_key_retrieval_request() + .call(index, &do_call) + .map_err(|error| format!("{}", error)) + .and_then(|server_key_id| contract.functions() + .is_server_key_retrieval_response_required() + .call(server_key_id.clone(), self_address, &do_call) + .map(|not_confirmed| ( + not_confirmed, + ServiceTask::RetrieveServerKey( + contract_address.clone(), + server_key_id, + ))) + .map_err(|error| format!("{}", error))) + } +} + +impl DocumentKeyStoreService { + /// Parse request log entry. + pub fn parse_log(origin: &Address, contract: &service::Service, raw_log: RawLog) -> Result { + let event = contract.events().document_key_store_requested(); + match event.parse_log(raw_log) { + Ok(l) => Ok(ServiceTask::StoreDocumentKey(origin.clone(), l.server_key_id, l.author, (*l.common_point).into(), (*l.encrypted_point).into())), + Err(e) => Err(format!("{}", e)), + } + } + + /// Check if response from key server is required. + pub fn is_response_required(client: &Client, contract_address: &Address, contract: &service::Service, server_key_id: &ServerKeyId, key_server: &Address) -> bool { + // we're checking confirmation in Latest block, because we're interested in latest contract state here + let do_call = |data| client.call_contract(BlockId::Latest, *contract_address, data); + contract.functions() + .is_document_key_store_response_required() + .call(*server_key_id, key_server.clone(), &do_call) + .unwrap_or(true) + } + + /// Prepare publish key transaction data. + pub fn prepare_pubish_tx_data(contract: &service::Service, server_key_id: &ServerKeyId) -> Bytes { + contract.functions() + .document_key_stored() + .input(*server_key_id) + } + + /// Prepare error transaction data. + pub fn prepare_error_tx_data(contract: &service::Service, server_key_id: &ServerKeyId) -> Bytes { + contract.functions() + .document_key_store_error() + .input(*server_key_id) + } + + /// Read pending requests count. + fn read_pending_requests_count(client: &Client, contract_address: &Address, _contract: &service::Service, block: &BlockId) -> Result { + let do_call = |data| client.call_contract(block.clone(), contract_address.clone(), data); + let contract = service::Service::default(); + contract.functions() + .document_key_store_requests_count() + .call(&do_call) + .map_err(|error| format!("{}", error)) + } + + /// Read pending request. + fn read_pending_request(self_key_pair: &NodeKeyPair, client: &Client, contract_address: &Address, contract: &service::Service, block: &BlockId, index: U256) -> Result<(bool, ServiceTask), String> { + let self_address = public_to_address(self_key_pair.public()); + let do_call = |d| client.call_contract(block.clone(), contract_address.clone(), d); + contract.functions() + .get_document_key_store_request() + .call(index, &do_call) + .map_err(|error| format!("{}", error)) + .and_then(|(server_key_id, author, common_point, encrypted_point)| contract.functions() + .is_document_key_store_response_required() + .call(server_key_id.clone(), self_address, &do_call) + .map(|not_confirmed| ( + not_confirmed, + ServiceTask::StoreDocumentKey( + contract_address.clone(), + server_key_id, + author, + Public::from_slice(&common_point), + Public::from_slice(&encrypted_point), + ))) + .map_err(|error| format!("{}", error))) + } +} + +impl DocumentKeyShadowRetrievalService { + /// Parse common request log entry. + pub fn parse_common_request_log(origin: &Address, contract: &service::Service, raw_log: RawLog) -> Result { + let event = contract.events().document_key_common_retrieval_requested(); + match event.parse_log(raw_log) { + Ok(l) => Ok(ServiceTask::RetrieveShadowDocumentKeyCommon(origin.clone(), l.server_key_id, l.requester)), + Err(e) => Err(format!("{}", e)), + } + } + + /// Parse personal request log entry. + pub fn parse_personal_request_log(origin: &Address, contract: &service::Service, raw_log: RawLog) -> Result { + let event = contract.events().document_key_personal_retrieval_requested(); + match event.parse_log(raw_log) { + Ok(l) => Ok(ServiceTask::RetrieveShadowDocumentKeyPersonal(origin.clone(), l.server_key_id, (*l.requester_public).into())), + Err(e) => Err(format!("{}", e)), + } + } + + /// Check if response from key server is required. + pub fn is_response_required(client: &Client, contract_address: &Address, contract: &service::Service, server_key_id: &ServerKeyId, requester: &Address, key_server: &Address) -> bool { + // we're checking confirmation in Latest block, because we're interested in latest contract state here + let do_call = |data| client.call_contract(BlockId::Latest, *contract_address, data); + contract.functions() + .is_document_key_shadow_retrieval_response_required() + .call(*server_key_id, *requester, key_server.clone(), &do_call) + .unwrap_or(true) + } + + /// Prepare publish common key transaction data. + pub fn prepare_pubish_common_tx_data(contract: &service::Service, server_key_id: &ServerKeyId, requester: &Address, common_point: Public, threshold: U256) -> Bytes { + contract.functions() + .document_key_common_retrieved() + .input(*server_key_id, *requester, common_point.to_vec(), threshold) + } + + /// Prepare publish personal key transaction data. + pub fn prepare_pubish_personal_tx_data(client: &Client, contract_address: &Address, contract: &service::Service, server_key_id: &ServerKeyId, requester: &Address, participants: &[Address], decrypted_secret: Public, shadow: Bytes) -> Result { + let mut participants_mask = U256::default(); + for participant in participants { + let participant_index = Self::map_key_server_address(client, contract_address, contract, participant.clone()) + .map_err(|e| format!("Error searching for {} participant: {}", participant, e))?; + participants_mask = participants_mask | (U256::one() << participant_index.into()); + } + Ok(contract.functions() + .document_key_personal_retrieved() + .input(*server_key_id, *requester, participants_mask, decrypted_secret.to_vec(), shadow)) + } + + /// Prepare error transaction data. + pub fn prepare_error_tx_data(contract: &service::Service, server_key_id: &ServerKeyId, requester: &Address) -> Bytes { + contract.functions() + .document_key_shadow_retrieval_error() + .input(*server_key_id, *requester) + } + + /// Read pending requests count. + fn read_pending_requests_count(client: &Client, contract_address: &Address, _contract: &service::Service, block: &BlockId) -> Result { + let do_call = |data| client.call_contract(block.clone(), contract_address.clone(), data); + let contract = service::Service::default(); + contract.functions() + .document_key_shadow_retrieval_requests_count() + .call(&do_call) + .map_err(|error| format!("{}", error)) + } + + /// Read pending request. + fn read_pending_request(self_key_pair: &NodeKeyPair, client: &Client, contract_address: &Address, contract: &service::Service, block: &BlockId, index: U256) -> Result<(bool, ServiceTask), String> { + let self_address = public_to_address(self_key_pair.public()); + let do_call = |d| client.call_contract(block.clone(), contract_address.clone(), d); + contract.functions() + .get_document_key_shadow_retrieval_request() + .call(index, &do_call) + .map_err(|error| format!("{}", error)) + .and_then(|(server_key_id, requester, is_common_retrieval_completed)| { + let requester = Public::from_slice(&requester); + contract.functions() + .is_document_key_shadow_retrieval_response_required() + .call(server_key_id.clone(), public_to_address(&requester), self_address, &do_call) + .map(|not_confirmed| ( + not_confirmed, + match is_common_retrieval_completed { + true => ServiceTask::RetrieveShadowDocumentKeyPersonal( + contract_address.clone(), + server_key_id, + requester, + ), + false => ServiceTask::RetrieveShadowDocumentKeyCommon( + contract_address.clone(), + server_key_id, + public_to_address(&requester), + ), + }, + )) + .map_err(|error| format!("{}", error)) + }) + } + + /// Map from key server address to key server index. + fn map_key_server_address(client: &Client, contract_address: &Address, contract: &service::Service, key_server: Address) -> Result { + // we're checking confirmation in Latest block, because tx ,ust be appended to the latest state + let do_call = |data| client.call_contract(BlockId::Latest, *contract_address, data); + contract.functions() + .require_key_server() + .call(key_server, &do_call) + .map_err(|e| format!("{}", e)) + .and_then(|index| if index > ::std::u8::MAX.into() { + Err(format!("key server index is too big: {}", index)) + } else { + let index: u32 = index.into(); + Ok(index as u8) }) - .unwrap_or(None) } } -/// Get hash of the last block with at least n confirmations. -fn get_confirmed_block_hash(client: &Client, confirmations: u64) -> Option { - client.block_number(BlockId::Latest) - .map(|b| b.saturating_sub(confirmations)) - .and_then(|b| client.block_hash(BlockId::Number(b))) +/// Parse threshold (we only supposrt 256 KS at max). +fn parse_threshold(threshold: U256) -> Result { + let threshold_num = threshold.low_u64(); + if threshold != threshold_num.into() || threshold_num >= ::std::u8::MAX as u64 { + return Err(format!("invalid threshold to use in service contract: {}", threshold)); + } + + Ok(threshold_num as usize) +} + +/// Serialize threshold (we only support 256 KS at max). +fn serialize_threshold(threshold: usize) -> Result { + if threshold > ::std::u8::MAX as usize { + return Err(format!("invalid threshold to use in service contract: {}", threshold)); + } + Ok(threshold.into()) } #[cfg(test)] pub mod tests { use parking_lot::Mutex; + use bytes::Bytes; use ethkey::Public; - use ethereum_types::H256; + use ethereum_types::Address; use listener::service_contract_listener::ServiceTask; - use ServerKeyId; + use {ServerKeyId}; use super::ServiceContract; #[derive(Default)] pub struct DummyServiceContract { pub is_actual: bool, - pub logs: Vec>, + pub logs: Vec, pub pending_requests: Vec<(bool, ServiceTask)>, - pub published_keys: Mutex>, + pub generated_server_keys: Mutex>, + pub server_keys_generation_failures: Mutex>, + pub retrieved_server_keys: Mutex>, + pub server_keys_retrieval_failures: Mutex>, + pub stored_document_keys: Mutex>, + pub document_keys_store_failures: Mutex>, + pub common_shadow_retrieved_document_keys: Mutex>, + pub personal_shadow_retrieved_document_keys: Mutex, Public, Bytes)>>, + pub document_keys_shadow_retrieval_failures: Mutex>, } impl ServiceContract for DummyServiceContract { @@ -340,7 +813,7 @@ pub mod tests { true } - fn read_logs(&self) -> Box>> { + fn read_logs(&self) -> Box> { Box::new(self.logs.clone().into_iter()) } @@ -348,8 +821,48 @@ pub mod tests { Box::new(self.pending_requests.clone().into_iter()) } - fn publish_server_key(&self, server_key_id: &ServerKeyId, server_key: &Public) -> Result<(), String> { - self.published_keys.lock().push((server_key_id.clone(), server_key.clone())); + fn publish_generated_server_key(&self, _origin: &Address, server_key_id: &ServerKeyId, server_key: Public) -> Result<(), String> { + self.generated_server_keys.lock().push((server_key_id.clone(), server_key.clone())); + Ok(()) + } + + fn publish_server_key_generation_error(&self, _origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String> { + self.server_keys_generation_failures.lock().push(server_key_id.clone()); + Ok(()) + } + + fn publish_retrieved_server_key(&self, _origin: &Address, server_key_id: &ServerKeyId, server_key: Public, threshold: usize) -> Result<(), String> { + self.retrieved_server_keys.lock().push((server_key_id.clone(), server_key.clone(), threshold)); + Ok(()) + } + + fn publish_server_key_retrieval_error(&self, _origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String> { + self.server_keys_retrieval_failures.lock().push(server_key_id.clone()); + Ok(()) + } + + fn publish_stored_document_key(&self, _origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String> { + self.stored_document_keys.lock().push(server_key_id.clone()); + Ok(()) + } + + fn publish_document_key_store_error(&self, _origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String> { + self.document_keys_store_failures.lock().push(server_key_id.clone()); + Ok(()) + } + + fn publish_retrieved_document_key_common(&self, _origin: &Address, server_key_id: &ServerKeyId, requester: &Address, common_point: Public, threshold: usize) -> Result<(), String> { + self.common_shadow_retrieved_document_keys.lock().push((server_key_id.clone(), requester.clone(), common_point.clone(), threshold)); + Ok(()) + } + + fn publish_retrieved_document_key_personal(&self, _origin: &Address, server_key_id: &ServerKeyId, requester: &Address, participants: &[Address], decrypted_secret: Public, shadow: Bytes) -> Result<(), String> { + self.personal_shadow_retrieved_document_keys.lock().push((server_key_id.clone(), requester.clone(), participants.iter().cloned().collect(), decrypted_secret, shadow)); + Ok(()) + } + + fn publish_document_key_retrieval_error(&self, _origin: &Address, server_key_id: &ServerKeyId, requester: &Address) -> Result<(), String> { + self.document_keys_shadow_retrieval_failures.lock().push((server_key_id.clone(), requester.clone())); Ok(()) } } diff --git a/secret_store/src/listener/service_contract_aggregate.rs b/secret_store/src/listener/service_contract_aggregate.rs new file mode 100644 index 0000000000000000000000000000000000000000..cc2e97b8d424c67ed33e6ec4fff64fa32e59e1ff --- /dev/null +++ b/secret_store/src/listener/service_contract_aggregate.rs @@ -0,0 +1,100 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use std::sync::Arc; +use bytes::Bytes; +use ethereum_types::Address; +use ethkey::Public; +use listener::service_contract::ServiceContract; +use listener::service_contract_listener::ServiceTask; +use {ServerKeyId}; + +/// Aggregated on-chain service contract. +pub struct OnChainServiceContractAggregate { + /// All hosted service contracts. + contracts: Vec>, +} + +impl OnChainServiceContractAggregate { + /// Create new aggregated service contract listener. + pub fn new(contracts: Vec>) -> Self { + debug_assert!(contracts.len() > 1); + OnChainServiceContractAggregate { + contracts: contracts, + } + } +} + +impl ServiceContract for OnChainServiceContractAggregate { + fn update(&self) -> bool { + let mut result = false; + for contract in &self.contracts { + result = contract.update() || result; + } + result + } + + fn read_logs(&self) -> Box> { + self.contracts.iter() + .fold(Box::new(::std::iter::empty()) as Box>, |i, c| + Box::new(i.chain(c.read_logs()))) + } + + fn read_pending_requests(&self) -> Box> { + self.contracts.iter() + .fold(Box::new(::std::iter::empty()) as Box>, |i, c| + Box::new(i.chain(c.read_pending_requests()))) + } + + // in current implementation all publish methods are independent of actual contract adddress + // (tx is sent to origin) => we do not care which contract to use for publish data in methods below + + fn publish_generated_server_key(&self, origin: &Address, server_key_id: &ServerKeyId, server_key: Public) -> Result<(), String> { + self.contracts[0].publish_generated_server_key(origin, server_key_id, server_key) + } + + fn publish_server_key_generation_error(&self, origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String> { + self.contracts[0].publish_server_key_generation_error(origin, server_key_id) + } + + fn publish_retrieved_server_key(&self, origin: &Address, server_key_id: &ServerKeyId, server_key: Public, threshold: usize) -> Result<(), String> { + self.contracts[0].publish_retrieved_server_key(origin, server_key_id, server_key, threshold) + } + + fn publish_server_key_retrieval_error(&self, origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String> { + self.contracts[0].publish_server_key_retrieval_error(origin, server_key_id) + } + + fn publish_stored_document_key(&self, origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String> { + self.contracts[0].publish_stored_document_key(origin, server_key_id) + } + + fn publish_document_key_store_error(&self, origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String> { + self.contracts[0].publish_document_key_store_error(origin, server_key_id) + } + + fn publish_retrieved_document_key_common(&self, origin: &Address, server_key_id: &ServerKeyId, requester: &Address, common_point: Public, threshold: usize) -> Result<(), String> { + self.contracts[0].publish_retrieved_document_key_common(origin, server_key_id, requester, common_point, threshold) + } + + fn publish_retrieved_document_key_personal(&self, origin: &Address, server_key_id: &ServerKeyId, requester: &Address, participants: &[Address], decrypted_secret: Public, shadow: Bytes) -> Result<(), String> { + self.contracts[0].publish_retrieved_document_key_personal(origin, server_key_id, requester, participants, decrypted_secret, shadow) + } + + fn publish_document_key_retrieval_error(&self, origin: &Address, server_key_id: &ServerKeyId, requester: &Address) -> Result<(), String> { + self.contracts[0].publish_document_key_retrieval_error(origin, server_key_id, requester) + } +} diff --git a/secret_store/src/listener/service_contract_listener.rs b/secret_store/src/listener/service_contract_listener.rs index 0d04a7daed98a63746448d8b5255258ad26402e1..8dc549a729fafcb4f92ced5412081b2f363d6c26 100644 --- a/secret_store/src/listener/service_contract_listener.rs +++ b/secret_store/src/listener/service_contract_listener.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -17,19 +17,26 @@ use std::collections::HashSet; use std::sync::Arc; use std::sync::atomic::{AtomicUsize, Ordering}; +use std::time::Duration; use std::thread; use parking_lot::Mutex; -use ethcore::client::ChainNotify; -use ethkey::{Random, Generator, Public, sign}; +use ethcore::client::{ChainNotify, ChainRoute}; +use ethkey::{Public, public_to_address}; use bytes::Bytes; -use ethereum_types::{H256, U256}; +use ethereum_types::{H256, U256, Address}; use key_server_set::KeyServerSet; -use key_server_cluster::{ClusterClient, ClusterSessionsListener, ClusterSession}; +use key_server_cluster::{NodeId, ClusterClient, ClusterSessionsListener, ClusterSession}; +use key_server_cluster::math; use key_server_cluster::generation_session::SessionImpl as GenerationSession; +use key_server_cluster::encryption_session::{check_encrypted_data, update_encrypted_data}; +use key_server_cluster::decryption_session::SessionImpl as DecryptionSession; +use key_server_cluster::key_version_negotiation_session::{SessionImpl as KeyVersionNegotiationSession, + IsolatedSessionTransport as KeyVersionNegotiationTransport, FailedContinueAction}; use key_storage::KeyStorage; +use acl_storage::AclStorage; use listener::service_contract::ServiceContract; use listener::tasks_queue::TasksQueue; -use {ServerKeyId, NodeKeyPair, KeyServer}; +use {ServerKeyId, NodeKeyPair, Error}; /// Retry interval (in blocks). Every RETRY_INTERVAL_BLOCKS blocks each KeyServer reads pending requests from /// service contract && tries to re-execute. The reason to have this mechanism is primarily because keys @@ -56,12 +63,12 @@ pub struct ServiceContractListener { pub struct ServiceContractListenerParams { /// Service contract. pub contract: Arc, - /// Key server reference. - pub key_server: Arc, /// This node key pair. pub self_key_pair: Arc, /// Key servers set. pub key_server_set: Arc, + /// ACL storage reference. + pub acl_storage: Arc, /// Cluster reference. pub cluster: Arc, /// Key storage reference. @@ -78,8 +85,10 @@ struct ServiceContractListenerData { pub tasks_queue: Arc>, /// Service contract. pub contract: Arc, - /// Key server reference. - pub key_server: Arc, + /// ACL storage reference. + pub acl_storage: Arc, + /// Cluster client reference. + pub cluster: Arc, /// This node key pair. pub self_key_pair: Arc, /// Key servers set. @@ -92,8 +101,10 @@ struct ServiceContractListenerData { /// Retry-related data. #[derive(Default)] struct ServiceContractRetryData { - /// Server keys, which we have generated (or tried to generate) since last retry moment. - pub generated_keys: HashSet, + /// Server keys, which we have 'touched' since last retry. + pub affected_server_keys: HashSet, + /// Document keys + requesters, which we have 'touched' since last retry. + pub affected_document_keys: HashSet<(ServerKeyId, Address)>, } /// Service task. @@ -101,67 +112,95 @@ struct ServiceContractRetryData { pub enum ServiceTask { /// Retry all 'stalled' tasks. Retry, - /// Generate server key (server_key_id, threshold). - GenerateServerKey(H256, H256), - /// Confirm server key (server_key_id). - RestoreServerKey(H256), + /// Generate server key (origin, server_key_id, author, threshold). + GenerateServerKey(Address, ServerKeyId, Address, usize), + /// Retrieve server key (origin, server_key_id). + RetrieveServerKey(Address, ServerKeyId), + /// Store document key (origin, server_key_id, author, common_point, encrypted_point). + StoreDocumentKey(Address, ServerKeyId, Address, Public, Public), + /// Retrieve common data of document key (origin, server_key_id, requester). + RetrieveShadowDocumentKeyCommon(Address, ServerKeyId, Address), + /// Retrieve personal data of document key (origin, server_key_id, requester). + RetrieveShadowDocumentKeyPersonal(Address, ServerKeyId, Public), /// Shutdown listener. Shutdown, } impl ServiceContractListener { /// Create new service contract listener. - pub fn new(params: ServiceContractListenerParams) -> Arc { + pub fn new(params: ServiceContractListenerParams) -> Result, Error> { let data = Arc::new(ServiceContractListenerData { last_retry: AtomicUsize::new(0), retry_data: Default::default(), tasks_queue: Arc::new(TasksQueue::new()), contract: params.contract, - key_server: params.key_server, + acl_storage: params.acl_storage, + cluster: params.cluster, self_key_pair: params.self_key_pair, key_server_set: params.key_server_set, key_storage: params.key_storage, }); - data.tasks_queue.push(ServiceTask::Retry); // we are not starting thread when in test mode let service_handle = if cfg!(test) { None } else { let service_thread_data = data.clone(); - Some(thread::spawn(move || Self::run_service_thread(service_thread_data))) + Some(thread::Builder::new().name("ServiceContractListener".into()).spawn(move || + Self::run_service_thread(service_thread_data)).map_err(|e| Error::Internal(format!("{}", e)))?) }; let contract = Arc::new(ServiceContractListener { data: data, service_handle: service_handle, }); - params.cluster.add_generation_listener(contract.clone()); - contract + contract.data.cluster.add_generation_listener(contract.clone()); + contract.data.cluster.add_decryption_listener(contract.clone()); + contract.data.cluster.add_key_version_negotiation_listener(contract.clone()); + Ok(contract) } /// Process incoming events of service contract. fn process_service_contract_events(&self) { + // shortcut: do not process events if we're isolated from the cluster + if self.data.key_server_set.is_isolated() { + return; + } + self.data.tasks_queue.push_many(self.data.contract.read_logs() - .filter_map(|topics| match topics.len() { - // when key is already generated && we have this key - 3 if self.data.key_storage.get(&topics[1]).map(|k| k.is_some()).unwrap_or_default() => { - Some(ServiceTask::RestoreServerKey( - topics[1], - )) - } - // when key is not yet generated && this node should be master of this key generation session - 3 if is_processed_by_this_key_server(&*self.data.key_server_set, &*self.data.self_key_pair, &topics[1]) => { - Some(ServiceTask::GenerateServerKey( - topics[1], - topics[2], - )) - }, - 3 => None, - l @ _ => { - warn!(target: "secretstore", "Ignoring ServerKeyRequested event with wrong number of params {}", l); - None - }, - })); + .filter_map(|task| Self::filter_task(&self.data, task))); + } + + /// Filter service task. Only returns Some if task must be executed by this server. + fn filter_task(data: &Arc, task: ServiceTask) -> Option { + match task { + // when this node should be master of this server key generation session + ServiceTask::GenerateServerKey(origin, server_key_id, author, threshold) if is_processed_by_this_key_server( + &*data.key_server_set, data.self_key_pair.public(), &server_key_id) => + Some(ServiceTask::GenerateServerKey(origin, server_key_id, author, threshold)), + // when server key is not yet generated and generation must be initiated by other node + ServiceTask::GenerateServerKey(_, _, _, _) => None, + + // when server key retrieval is requested + ServiceTask::RetrieveServerKey(origin, server_key_id) => + Some(ServiceTask::RetrieveServerKey(origin, server_key_id)), + + // when document key store is requested + ServiceTask::StoreDocumentKey(origin, server_key_id, author, common_point, encrypted_point) => + Some(ServiceTask::StoreDocumentKey(origin, server_key_id, author, common_point, encrypted_point)), + + // when common document key data retrieval is requested + ServiceTask::RetrieveShadowDocumentKeyCommon(origin, server_key_id, requester) => + Some(ServiceTask::RetrieveShadowDocumentKeyCommon(origin, server_key_id, requester)), + + // when this node should be master of this document key decryption session + ServiceTask::RetrieveShadowDocumentKeyPersonal(origin, server_key_id, requester) if is_processed_by_this_key_server( + &*data.key_server_set, data.self_key_pair.public(), &server_key_id) => + Some(ServiceTask::RetrieveShadowDocumentKeyPersonal(origin, server_key_id, requester)), + // when server key is not yet generated and generation must be initiated by other node + ServiceTask::RetrieveShadowDocumentKeyPersonal(_, _, _) => None, + + ServiceTask::Retry | ServiceTask::Shutdown => unreachable!("must be filtered outside"), + } } /// Service thread procedure. @@ -172,18 +211,45 @@ impl ServiceContractListener { match task { ServiceTask::Shutdown => break, - task @ _ => { - // the only possible reaction to an error is a trace && it is already happened + task => { + // the only possible reaction to an error is a tx+trace && it is already happened let _ = Self::process_service_task(&data, task); }, }; } + + trace!(target: "secretstore", "{}: ServiceContractListener thread stopped", data.self_key_pair.public()); } /// Process single service task. fn process_service_task(data: &Arc, task: ServiceTask) -> Result<(), String> { - match task { - ServiceTask::Retry => + match &task { + &ServiceTask::GenerateServerKey(origin, server_key_id, author, threshold) => { + data.retry_data.lock().affected_server_keys.insert(server_key_id.clone()); + log_service_task_result(&task, data.self_key_pair.public(), + Self::generate_server_key(&data, origin, &server_key_id, author, threshold)) + }, + &ServiceTask::RetrieveServerKey(origin, server_key_id) => { + data.retry_data.lock().affected_server_keys.insert(server_key_id.clone()); + log_service_task_result(&task, data.self_key_pair.public(), + Self::retrieve_server_key(&data, origin, &server_key_id)) + }, + &ServiceTask::StoreDocumentKey(origin, server_key_id, author, common_point, encrypted_point) => { + data.retry_data.lock().affected_document_keys.insert((server_key_id.clone(), author.clone())); + log_service_task_result(&task, data.self_key_pair.public(), + Self::store_document_key(&data, origin, &server_key_id, &author, &common_point, &encrypted_point)) + }, + &ServiceTask::RetrieveShadowDocumentKeyCommon(origin, server_key_id, requester) => { + data.retry_data.lock().affected_document_keys.insert((server_key_id.clone(), requester.clone())); + log_service_task_result(&task, data.self_key_pair.public(), + Self::retrieve_document_key_common(&data, origin, &server_key_id, &requester)) + }, + &ServiceTask::RetrieveShadowDocumentKeyPersonal(origin, server_key_id, requester) => { + data.retry_data.lock().affected_server_keys.insert(server_key_id.clone()); + log_service_task_result(&task, data.self_key_pair.public(), + Self::retrieve_document_key_personal(&data, origin, &server_key_id, requester)) + }, + &ServiceTask::Retry => { Self::retry_pending_requests(&data) .map(|processed_requests| { if processed_requests != 0 { @@ -196,38 +262,9 @@ impl ServiceContractListener { warn!(target: "secretstore", "{}: retrying pending requests has failed with: {}", data.self_key_pair.public(), error); error - }), - ServiceTask::RestoreServerKey(server_key_id) => { - data.retry_data.lock().generated_keys.insert(server_key_id.clone()); - Self::restore_server_key(&data, &server_key_id) - .and_then(|server_key| Self::publish_server_key(&data, &server_key_id, &server_key)) - .map(|_| { - trace!(target: "secretstore", "{}: processed RestoreServerKey({}) request", - data.self_key_pair.public(), server_key_id); - () - }) - .map_err(|error| { - warn!(target: "secretstore", "{}: failed to process RestoreServerKey({}) request with: {}", - data.self_key_pair.public(), server_key_id, error); - error - }) - }, - ServiceTask::GenerateServerKey(server_key_id, threshold) => { - data.retry_data.lock().generated_keys.insert(server_key_id.clone()); - Self::generate_server_key(&data, &server_key_id, &threshold) - .and_then(|server_key| Self::publish_server_key(&data, &server_key_id, &server_key)) - .map(|_| { - trace!(target: "secretstore", "{}: processed GenerateServerKey({}, {}) request", - data.self_key_pair.public(), server_key_id, threshold); - () - }) - .map_err(|error| { - warn!(target: "secretstore", "{}: failed to process GenerateServerKey({}, {}) request with: {}", - data.self_key_pair.public(), server_key_id, threshold, error); - error }) }, - ServiceTask::Shutdown => unreachable!("it must be checked outside"), + &ServiceTask::Shutdown => unreachable!("must be filtered outside"), } } @@ -236,32 +273,28 @@ impl ServiceContractListener { let mut failed_requests = 0; let mut processed_requests = 0; let retry_data = ::std::mem::replace(&mut *data.retry_data.lock(), Default::default()); - for (is_confirmed, task) in data.contract.read_pending_requests() { + let pending_tasks = data.contract.read_pending_requests() + .filter_map(|(is_confirmed, task)| Self::filter_task(data, task) + .map(|t| (is_confirmed, t))); + for (is_response_required, task) in pending_tasks { // only process requests, which we haven't confirmed yet - if is_confirmed { + if !is_response_required { continue; } - let request_result = match task { - ServiceTask::GenerateServerKey(server_key_id, threshold) => { - // only process request, which haven't been processed recently - // there could be a lag when we've just generated server key && retrying on the same block - // (or before our tx is mined) - state is not updated yet - if retry_data.generated_keys.contains(&server_key_id) { - continue; - } - - // process request - let is_own_request = is_processed_by_this_key_server(&*data.key_server_set, &*data.self_key_pair, &server_key_id); - Self::process_service_task(data, match is_own_request { - true => ServiceTask::GenerateServerKey(server_key_id, threshold.into()), - false => ServiceTask::RestoreServerKey(server_key_id), - }) - }, - _ => Err("not supported".into()), - }; + match task { + ServiceTask::GenerateServerKey(_, ref key, _, _) | ServiceTask::RetrieveServerKey(_, ref key) + if retry_data.affected_server_keys.contains(key) => continue, + ServiceTask::StoreDocumentKey(_, ref key, ref author, _, _) | + ServiceTask::RetrieveShadowDocumentKeyCommon(_, ref key, ref author) + if retry_data.affected_document_keys.contains(&(key.clone(), author.clone())) => continue, + ServiceTask::RetrieveShadowDocumentKeyPersonal(_, ref key, ref requester) + if retry_data.affected_document_keys.contains(&(key.clone(), public_to_address(requester))) => continue, + _ => (), + } // process request result + let request_result = Self::process_service_task(data, task); match request_result { Ok(_) => processed_requests += 1, Err(_) => { @@ -276,33 +309,118 @@ impl ServiceContractListener { Ok(processed_requests) } - /// Generate server key. - fn generate_server_key(data: &Arc, server_key_id: &ServerKeyId, threshold: &H256) -> Result { - let threshold_num = threshold.low_u64(); - if threshold != &threshold_num.into() || threshold_num >= ::std::usize::MAX as u64 { - return Err(format!("invalid threshold {:?}", threshold)); + /// Generate server key (start generation session). + fn generate_server_key(data: &Arc, origin: Address, server_key_id: &ServerKeyId, author: Address, threshold: usize) -> Result<(), String> { + Self::process_server_key_generation_result(data, origin, server_key_id, data.cluster.new_generation_session( + server_key_id.clone(), Some(origin), author, threshold).map(|_| None).map_err(Into::into)) + } + + /// Process server key generation result. + fn process_server_key_generation_result(data: &Arc, origin: Address, server_key_id: &ServerKeyId, result: Result, Error>) -> Result<(), String> { + match result { + Ok(None) => Ok(()), + Ok(Some(server_key)) => { + data.contract.publish_generated_server_key(&origin, server_key_id, server_key) + }, + Err(ref error) if error.is_non_fatal() => Err(format!("{}", error)), + Err(ref error) => { + // ignore error as we're already processing an error + let _ = data.contract.publish_server_key_generation_error(&origin, server_key_id) + .map_err(|error| warn!(target: "secretstore", "{}: failed to publish GenerateServerKey({}) error: {}", + data.self_key_pair.public(), server_key_id, error)); + Err(format!("{}", error)) + } } + } - // key server expects signed server_key_id in server_key_generation procedure - // only signer could store document key for this server key later - // => this API (server key generation) is not suitable for usage in encryption via contract endpoint - let author_key = Random.generate().map_err(|e| format!("{}", e))?; - let server_key_id_signature = sign(author_key.secret(), server_key_id).map_err(|e| format!("{}", e))?; - data.key_server.generate_key(server_key_id, &server_key_id_signature, threshold_num as usize) - .map_err(Into::into) + /// Retrieve server key. + fn retrieve_server_key(data: &Arc, origin: Address, server_key_id: &ServerKeyId) -> Result<(), String> { + match data.key_storage.get(server_key_id) { + Ok(Some(server_key_share)) => { + data.contract.publish_retrieved_server_key(&origin, server_key_id, server_key_share.public, server_key_share.threshold) + }, + Ok(None) => { + data.contract.publish_server_key_retrieval_error(&origin, server_key_id) + } + Err(ref error) if error.is_non_fatal() => Err(format!("{}", error)), + Err(ref error) => { + // ignore error as we're already processing an error + let _ = data.contract.publish_server_key_retrieval_error(&origin, server_key_id) + .map_err(|error| warn!(target: "secretstore", "{}: failed to publish RetrieveServerKey({}) error: {}", + data.self_key_pair.public(), server_key_id, error)); + Err(format!("{}", error)) + } + } + } + + /// Store document key. + fn store_document_key(data: &Arc, origin: Address, server_key_id: &ServerKeyId, author: &Address, common_point: &Public, encrypted_point: &Public) -> Result<(), String> { + let store_result = data.key_storage.get(server_key_id) + .and_then(|key_share| key_share.ok_or(Error::ServerKeyIsNotFound)) + .and_then(|key_share| check_encrypted_data(Some(&key_share)).map(|_| key_share).map_err(Into::into)) + .and_then(|key_share| update_encrypted_data(&data.key_storage, server_key_id.clone(), key_share, + author.clone(), common_point.clone(), encrypted_point.clone()).map_err(Into::into)); + match store_result { + Ok(()) => { + data.contract.publish_stored_document_key(&origin, server_key_id) + }, + Err(ref error) if error.is_non_fatal() => Err(format!("{}", error)), + Err(ref error) => { + // ignore error as we're already processing an error + let _ = data.contract.publish_document_key_store_error(&origin, server_key_id) + .map_err(|error| warn!(target: "secretstore", "{}: failed to publish StoreDocumentKey({}) error: {}", + data.self_key_pair.public(), server_key_id, error)); + Err(format!("{}", error)) + }, + } } - /// Restore server key. - fn restore_server_key(data: &Arc, server_key_id: &ServerKeyId) -> Result { - data.key_storage.get(server_key_id) - .map_err(|e| format!("{}", e)) - .and_then(|ks| ks.ok_or("missing key".to_owned())) - .map(|ks| ks.public) + /// Retrieve common part of document key. + fn retrieve_document_key_common(data: &Arc, origin: Address, server_key_id: &ServerKeyId, requester: &Address) -> Result<(), String> { + let retrieval_result = data.acl_storage.check(requester.clone(), server_key_id) + .and_then(|is_allowed| if !is_allowed { Err(Error::AccessDenied) } else { Ok(()) }) + .and_then(|_| data.key_storage.get(server_key_id).and_then(|key_share| key_share.ok_or(Error::ServerKeyIsNotFound))) + .and_then(|key_share| key_share.common_point + .ok_or(Error::DocumentKeyIsNotFound) + .and_then(|common_point| math::make_common_shadow_point(key_share.threshold, common_point)) + .map(|common_point| (common_point, key_share.threshold))); + match retrieval_result { + Ok((common_point, threshold)) => { + data.contract.publish_retrieved_document_key_common(&origin, server_key_id, requester, common_point, threshold) + }, + Err(ref error) if error.is_non_fatal() => Err(format!("{}", error)), + Err(ref error) => { + // ignore error as we're already processing an error + let _ = data.contract.publish_document_key_retrieval_error(&origin, server_key_id, requester) + .map_err(|error| warn!(target: "secretstore", "{}: failed to publish RetrieveDocumentKey({}) error: {}", + data.self_key_pair.public(), server_key_id, error)); + Err(format!("{}", error)) + }, + } } - /// Publish server key. - fn publish_server_key(data: &Arc, server_key_id: &ServerKeyId, server_key: &Public) -> Result<(), String> { - data.contract.publish_server_key(server_key_id, server_key) + /// Retrieve personal part of document key (start decryption session). + fn retrieve_document_key_personal(data: &Arc, origin: Address, server_key_id: &ServerKeyId, requester: Public) -> Result<(), String> { + Self::process_document_key_retrieval_result(data, origin, server_key_id, &public_to_address(&requester), data.cluster.new_decryption_session( + server_key_id.clone(), Some(origin), requester.clone().into(), None, true, true).map(|_| None).map_err(Into::into)) + } + + /// Process document key retrieval result. + fn process_document_key_retrieval_result(data: &Arc, origin: Address, server_key_id: &ServerKeyId, requester: &Address, result: Result, Public, Bytes)>, Error>) -> Result<(), String> { + match result { + Ok(None) => Ok(()), + Ok(Some((participants, decrypted_secret, shadow))) => { + data.contract.publish_retrieved_document_key_personal(&origin, server_key_id, &requester, &participants, decrypted_secret, shadow) + }, + Err(ref error) if error.is_non_fatal() => Err(format!("{}", error)), + Err(ref error) => { + // ignore error as we're already processing an error + let _ = data.contract.publish_document_key_retrieval_error(&origin, server_key_id, &requester) + .map_err(|error| warn!(target: "secretstore", "{}: failed to publish RetrieveDocumentKey({}) error: {}", + data.self_key_pair.public(), server_key_id, error)); + Err(format!("{}", error)) + } + } } } @@ -317,9 +435,9 @@ impl Drop for ServiceContractListener { } impl ChainNotify for ServiceContractListener { - fn new_blocks(&self, _imported: Vec, _invalid: Vec, enacted: Vec, _retracted: Vec, _sealed: Vec, _proposed: Vec, _duration: u64) { - let enacted_len = enacted.len(); - if enacted_len == 0 { + fn new_blocks(&self, _imported: Vec, _invalid: Vec, route: ChainRoute, _sealed: Vec, _proposed: Vec, _duration: Duration) { + let enacted_len = route.enacted().len(); + if enacted_len == 0 && route.retracted().is_empty() { return; } @@ -332,35 +450,117 @@ impl ChainNotify for ServiceContractListener { // schedule retry if received enough blocks since last retry // it maybe inaccurate when switching syncing/synced states, but that's ok if self.data.last_retry.fetch_add(enacted_len, Ordering::Relaxed) >= RETRY_INTERVAL_BLOCKS { - self.data.tasks_queue.push(ServiceTask::Retry); - self.data.last_retry.store(0, Ordering::Relaxed); + // shortcut: do not retry if we're isolated from the cluster + if !self.data.key_server_set.is_isolated() { + self.data.tasks_queue.push(ServiceTask::Retry); + self.data.last_retry.store(0, Ordering::Relaxed); + } } } } impl ClusterSessionsListener for ServiceContractListener { fn on_session_removed(&self, session: Arc) { - // only publish when the session is started by another node - // when it is started by this node, it is published from process_service_task - if !is_processed_by_this_key_server(&*self.data.key_server_set, &*self.data.self_key_pair, &session.id()) { - // by this time sesion must already be completed - either successfully, or not - assert!(session.is_finished()); - - // ignore result - the only thing that we can do is to log the error - match session.wait(Some(Default::default())) - .map_err(|e| format!("{}", e)) - .and_then(|server_key| Self::publish_server_key(&self.data, &session.id(), &server_key)) { - Ok(_) => trace!(target: "secretstore", "{}: completed foreign GenerateServerKey({}) request", - self.data.self_key_pair.public(), session.id()), - Err(error) => warn!(target: "secretstore", "{}: failed to process GenerateServerKey({}) request with: {}", - self.data.self_key_pair.public(), session.id(), error), + // by this time sesion must already be completed - either successfully, or not + assert!(session.is_finished()); + + // ignore result - the only thing that we can do is to log the error + let server_key_id = session.id(); + if let Some(origin) = session.origin() { + if let Some(generation_result) = session.wait(Some(Default::default())) { + let generation_result = generation_result.map(Some).map_err(Into::into); + let _ = Self::process_server_key_generation_result(&self.data, origin, &server_key_id, generation_result); } } } } -/// Returns true when session, related to `server_key_id` must be started on this KeyServer. -fn is_processed_by_this_key_server(key_server_set: &KeyServerSet, self_key_pair: &NodeKeyPair, server_key_id: &H256) -> bool { +impl ClusterSessionsListener for ServiceContractListener { + fn on_session_removed(&self, session: Arc) { + // by this time sesion must already be completed - either successfully, or not + assert!(session.is_finished()); + + // ignore result - the only thing that we can do is to log the error + let session_id = session.id(); + let server_key_id = session_id.id; + if let (Some(requester), Some(origin)) = (session.requester().and_then(|r| r.address(&server_key_id).ok()), session.origin()) { + if let Some(retrieval_result) = session.wait(Some(Default::default())) { + let retrieval_result = retrieval_result.map(|key_shadow| + session.broadcast_shadows() + .and_then(|broadcast_shadows| + broadcast_shadows.get(self.data.self_key_pair.public()) + .map(|self_shadow| ( + broadcast_shadows.keys().map(public_to_address).collect(), + key_shadow.decrypted_secret, + self_shadow.clone() + ))) + ).map_err(Into::into); + let _ = Self::process_document_key_retrieval_result(&self.data, origin, &server_key_id, &requester, retrieval_result); + } + } + } +} + +impl ClusterSessionsListener> for ServiceContractListener { + fn on_session_removed(&self, session: Arc>) { + // by this time sesion must already be completed - either successfully, or not + assert!(session.is_finished()); + + // we're interested in: + // 1) sessions failed with fatal error + // 2) with decryption continue action + let error = match session.wait() { + Err(ref error) if !error.is_non_fatal() => error.clone(), + _ => return, + }; + + let (origin, requester) = match session.take_failed_continue_action() { + Some(FailedContinueAction::Decrypt(Some(origin), requester)) => (origin, requester), + _ => return, + }; + + // check if master node is responsible for processing key requests + let meta = session.meta(); + if !is_processed_by_this_key_server(&*self.data.key_server_set, &meta.master_node_id, &meta.id) { + return; + } + + // ignore result as we're already processing an error + let _ = Self::process_document_key_retrieval_result(&self.data, origin, &meta.id, &requester, Err(error)); + } +} + +impl ::std::fmt::Display for ServiceTask { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + match *self { + ServiceTask::Retry => write!(f, "Retry"), + ServiceTask::GenerateServerKey(_, ref server_key_id, ref author, ref threshold) => + write!(f, "GenerateServerKey({}, {}, {})", server_key_id, author, threshold), + ServiceTask::RetrieveServerKey(_, ref server_key_id) => + write!(f, "RetrieveServerKey({})", server_key_id), + ServiceTask::StoreDocumentKey(_, ref server_key_id, ref author, _, _) => + write!(f, "StoreDocumentKey({}, {})", server_key_id, author), + ServiceTask::RetrieveShadowDocumentKeyCommon(_, ref server_key_id, ref requester) => + write!(f, "RetrieveShadowDocumentKeyCommon({}, {})", server_key_id, requester), + ServiceTask::RetrieveShadowDocumentKeyPersonal(_, ref server_key_id, ref requester) => + write!(f, "RetrieveShadowDocumentKeyPersonal({}, {})", server_key_id, public_to_address(requester)), + ServiceTask::Shutdown => write!(f, "Shutdown"), + } + } +} + +/// Log service task result. +fn log_service_task_result(task: &ServiceTask, self_id: &Public, result: Result<(), String>) -> Result<(), String> { + match result { + Ok(_) => trace!(target: "secretstore", "{}: processed {} request", self_id, task), + Err(ref error) => warn!(target: "secretstore", "{}: failed to process {} request with: {}", self_id, task, error), + } + + result +} + +/// Returns true when session, related to `server_key_id` must be started on `node`. +fn is_processed_by_this_key_server(key_server_set: &KeyServerSet, node: &NodeId, server_key_id: &H256) -> bool { let servers = key_server_set.snapshot().current_set; let total_servers_count = servers.len(); match total_servers_count { @@ -369,7 +569,7 @@ fn is_processed_by_this_key_server(key_server_set: &KeyServerSet, self_key_pair: _ => (), } - let this_server_index = match servers.keys().enumerate().find(|&(_, s)| s == self_key_pair.public()) { + let this_server_index = match servers.keys().enumerate().find(|&(_, s)| s == node) { Some((index, _)) => index, None => return false, }; @@ -390,41 +590,60 @@ mod tests { use listener::service_contract::ServiceContract; use listener::service_contract::tests::DummyServiceContract; use key_server_cluster::DummyClusterClient; - use key_server::tests::DummyKeyServer; + use acl_storage::{AclStorage, DummyAclStorage}; use key_storage::{KeyStorage, DocumentKeyShare}; use key_storage::tests::DummyKeyStorage; + use key_server_set::KeyServerSet; use key_server_set::tests::MapKeyServerSet; - use PlainNodeKeyPair; + use {NodeKeyPair, PlainNodeKeyPair, ServerKeyId}; use super::{ServiceTask, ServiceContractListener, ServiceContractListenerParams, is_processed_by_this_key_server}; - fn make_service_contract_listener(contract: Option>, key_server: Option>, key_storage: Option>) -> Arc { - let contract = contract.unwrap_or_else(|| Arc::new(DummyServiceContract::default())); - let key_server = key_server.unwrap_or_else(|| Arc::new(DummyKeyServer::default())); - let key_storage = key_storage.unwrap_or_else(|| Arc::new(DummyKeyStorage::default())); - let servers_set = Arc::new(MapKeyServerSet::new(vec![ + fn create_non_empty_key_storage(has_doc_key: bool) -> Arc { + let key_storage = Arc::new(DummyKeyStorage::default()); + let mut key_share = DocumentKeyShare::default(); + key_share.public = KeyPair::from_secret("0000000000000000000000000000000000000000000000000000000000000001" + .parse().unwrap()).unwrap().public().clone(); + if has_doc_key { + key_share.common_point = Some(Default::default()); + key_share.encrypted_point = Some(Default::default()); + } + key_storage.insert(Default::default(), key_share.clone()).unwrap(); + key_storage + } + + fn make_servers_set(is_isolated: bool) -> Arc { + Arc::new(MapKeyServerSet::new(is_isolated, vec![ ("79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8".parse().unwrap(), "127.0.0.1:8080".parse().unwrap()), ("c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a".parse().unwrap(), "127.0.0.1:8080".parse().unwrap()), ("f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672".parse().unwrap(), "127.0.0.1:8080".parse().unwrap()), - ].into_iter().collect())); + ].into_iter().collect())) + } + + fn make_service_contract_listener(contract: Option>, cluster: Option>, key_storage: Option>, acl_storage: Option>, servers_set: Option>) -> Arc { + let contract = contract.unwrap_or_else(|| Arc::new(DummyServiceContract::default())); + let cluster = cluster.unwrap_or_else(|| Arc::new(DummyClusterClient::default())); + let key_storage = key_storage.unwrap_or_else(|| Arc::new(DummyKeyStorage::default())); + let acl_storage = acl_storage.unwrap_or_else(|| Arc::new(DummyAclStorage::default())); + let servers_set = servers_set.unwrap_or_else(|| make_servers_set(false)); let self_key_pair = Arc::new(PlainNodeKeyPair::new(KeyPair::from_secret("0000000000000000000000000000000000000000000000000000000000000001".parse().unwrap()).unwrap())); ServiceContractListener::new(ServiceContractListenerParams { contract: contract, - key_server: key_server, self_key_pair: self_key_pair, key_server_set: servers_set, - cluster: Arc::new(DummyClusterClient::default()), + acl_storage: acl_storage, + cluster: cluster, key_storage: key_storage, - }) + }).unwrap() } #[test] fn is_not_processed_by_this_key_server_with_zero_servers() { assert_eq!(is_processed_by_this_key_server( &MapKeyServerSet::default(), - &PlainNodeKeyPair::new(Random.generate().unwrap()), + Random.generate().unwrap().public(), &Default::default()), false); } @@ -432,27 +651,27 @@ mod tests { fn is_processed_by_this_key_server_with_single_server() { let self_key_pair = Random.generate().unwrap(); assert_eq!(is_processed_by_this_key_server( - &MapKeyServerSet::new(vec![ + &MapKeyServerSet::new(false, vec![ (self_key_pair.public().clone(), "127.0.0.1:8080".parse().unwrap()) ].into_iter().collect()), - &PlainNodeKeyPair::new(self_key_pair), + self_key_pair.public(), &Default::default()), true); } #[test] fn is_not_processed_by_this_key_server_when_not_a_part_of_servers_set() { assert!(is_processed_by_this_key_server( - &MapKeyServerSet::new(vec![ + &MapKeyServerSet::new(false, vec![ (Random.generate().unwrap().public().clone(), "127.0.0.1:8080".parse().unwrap()) ].into_iter().collect()), - &PlainNodeKeyPair::new(Random.generate().unwrap()), + Random.generate().unwrap().public(), &Default::default())); } #[test] fn is_processed_by_this_key_server_in_set_of_3() { // servers set is ordered && server range depends on index of this server - let servers_set = MapKeyServerSet::new(vec![ + let servers_set = MapKeyServerSet::new(false, vec![ // secret: 0000000000000000000000000000000000000000000000000000000000000001 ("79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8".parse().unwrap(), "127.0.0.1:8080".parse().unwrap()), @@ -467,46 +686,46 @@ mod tests { // 1st server: process hashes [0x0; 0x555...555] let key_pair = PlainNodeKeyPair::new(KeyPair::from_secret( "0000000000000000000000000000000000000000000000000000000000000001".parse().unwrap()).unwrap()); - assert_eq!(is_processed_by_this_key_server(&servers_set, &key_pair, + assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), &"0000000000000000000000000000000000000000000000000000000000000000".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, &key_pair, + assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), &"3000000000000000000000000000000000000000000000000000000000000000".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, &key_pair, + assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), &"5555555555555555555555555555555555555555555555555555555555555555".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, &key_pair, + assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), &"5555555555555555555555555555555555555555555555555555555555555556".parse().unwrap()), false); // 2nd server: process hashes from 0x555...556 to 0xaaa...aab let key_pair = PlainNodeKeyPair::new(KeyPair::from_secret( "0000000000000000000000000000000000000000000000000000000000000002".parse().unwrap()).unwrap()); - assert_eq!(is_processed_by_this_key_server(&servers_set, &key_pair, + assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), &"5555555555555555555555555555555555555555555555555555555555555555".parse().unwrap()), false); - assert_eq!(is_processed_by_this_key_server(&servers_set, &key_pair, + assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), &"5555555555555555555555555555555555555555555555555555555555555556".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, &key_pair, + assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), &"7555555555555555555555555555555555555555555555555555555555555555".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, &key_pair, + assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), &"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, &key_pair, + assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), &"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac".parse().unwrap()), false); // 3rd server: process hashes from 0x800...000 to 0xbff...ff let key_pair = PlainNodeKeyPair::new(KeyPair::from_secret( "0000000000000000000000000000000000000000000000000000000000000003".parse().unwrap()).unwrap()); - assert_eq!(is_processed_by_this_key_server(&servers_set, &key_pair, + assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), &"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab".parse().unwrap()), false); - assert_eq!(is_processed_by_this_key_server(&servers_set, &key_pair, + assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), &"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, &key_pair, + assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), &"daaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, &key_pair, + assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), &"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".parse().unwrap()), true); } #[test] fn is_processed_by_this_key_server_in_set_of_4() { // servers set is ordered && server range depends on index of this server - let servers_set = MapKeyServerSet::new(vec![ + let servers_set = MapKeyServerSet::new(false, vec![ // secret: 0000000000000000000000000000000000000000000000000000000000000001 ("79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8".parse().unwrap(), "127.0.0.1:8080".parse().unwrap()), @@ -524,136 +743,316 @@ mod tests { // 1st server: process hashes [0x0; 0x3ff...ff] let key_pair = PlainNodeKeyPair::new(KeyPair::from_secret( "0000000000000000000000000000000000000000000000000000000000000001".parse().unwrap()).unwrap()); - assert_eq!(is_processed_by_this_key_server(&servers_set, &key_pair, + assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), &"0000000000000000000000000000000000000000000000000000000000000000".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, &key_pair, + assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), &"2000000000000000000000000000000000000000000000000000000000000000".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, &key_pair, + assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), &"3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, &key_pair, + assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), &"4000000000000000000000000000000000000000000000000000000000000000".parse().unwrap()), false); // 2nd server: process hashes from 0x400...000 to 0x7ff...ff let key_pair = PlainNodeKeyPair::new(KeyPair::from_secret( "0000000000000000000000000000000000000000000000000000000000000002".parse().unwrap()).unwrap()); - assert_eq!(is_processed_by_this_key_server(&servers_set, &key_pair, + assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), &"3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".parse().unwrap()), false); - assert_eq!(is_processed_by_this_key_server(&servers_set, &key_pair, + assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), &"4000000000000000000000000000000000000000000000000000000000000000".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, &key_pair, + assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), &"6000000000000000000000000000000000000000000000000000000000000000".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, &key_pair, + assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), &"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, &key_pair, + assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), &"8000000000000000000000000000000000000000000000000000000000000000".parse().unwrap()), false); // 3rd server: process hashes from 0x800...000 to 0xbff...ff let key_pair = PlainNodeKeyPair::new(KeyPair::from_secret( "0000000000000000000000000000000000000000000000000000000000000004".parse().unwrap()).unwrap()); - assert_eq!(is_processed_by_this_key_server(&servers_set, &key_pair, + assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), &"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".parse().unwrap()), false); - assert_eq!(is_processed_by_this_key_server(&servers_set, &key_pair, + assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), &"8000000000000000000000000000000000000000000000000000000000000000".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, &key_pair, + assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), &"a000000000000000000000000000000000000000000000000000000000000000".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, &key_pair, + assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), &"bfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, &key_pair, + assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), &"c000000000000000000000000000000000000000000000000000000000000000".parse().unwrap()), false); // 4th server: process hashes from 0xc00...000 to 0xfff...ff let key_pair = PlainNodeKeyPair::new(KeyPair::from_secret( "0000000000000000000000000000000000000000000000000000000000000003".parse().unwrap()).unwrap()); - assert_eq!(is_processed_by_this_key_server(&servers_set, &key_pair, + assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), &"bfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".parse().unwrap()), false); - assert_eq!(is_processed_by_this_key_server(&servers_set, &key_pair, + assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), &"c000000000000000000000000000000000000000000000000000000000000000".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, &key_pair, + assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), &"e000000000000000000000000000000000000000000000000000000000000000".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, &key_pair, + assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), &"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".parse().unwrap()), true); } #[test] fn no_tasks_scheduled_when_no_contract_events() { - let listener = make_service_contract_listener(None, None, None); - assert_eq!(listener.data.tasks_queue.snapshot().len(), 1); + let listener = make_service_contract_listener(None, None, None, None, None); + assert_eq!(listener.data.tasks_queue.snapshot().len(), 0); listener.process_service_contract_events(); - assert_eq!(listener.data.tasks_queue.snapshot().len(), 1); + assert_eq!(listener.data.tasks_queue.snapshot().len(), 0); } #[test] - fn server_key_generation_is_scheduled_when_requested_key_is_unknown() { + fn tasks_are_not_scheduled_on_isolated_node() { let mut contract = DummyServiceContract::default(); - contract.logs.push(vec![Default::default(), Default::default(), Default::default()]); - let listener = make_service_contract_listener(Some(Arc::new(contract)), None, None); - assert_eq!(listener.data.tasks_queue.snapshot().len(), 1); + contract.logs.push(ServiceTask::GenerateServerKey(Default::default(), Default::default(), Default::default(), 0)); + let listener = make_service_contract_listener(Some(Arc::new(contract)), None, None, None, Some(make_servers_set(true))); + assert_eq!(listener.data.tasks_queue.snapshot().len(), 0); listener.process_service_contract_events(); - assert_eq!(listener.data.tasks_queue.snapshot().len(), 2); - assert_eq!(listener.data.tasks_queue.snapshot().pop_back(), Some(ServiceTask::GenerateServerKey(Default::default(), Default::default()))); + assert_eq!(listener.data.tasks_queue.snapshot().len(), 0); } + // server key generation tests + #[test] - fn no_new_tasks_scheduled_when_requested_key_is_unknown_and_request_belongs_to_other_key_server() { + fn server_key_generation_is_scheduled_when_requested() { + let mut contract = DummyServiceContract::default(); + contract.logs.push(ServiceTask::GenerateServerKey(Default::default(), Default::default(), Default::default(), 0)); + let listener = make_service_contract_listener(Some(Arc::new(contract)), None, None, None, None); + assert_eq!(listener.data.tasks_queue.snapshot().len(), 0); + listener.process_service_contract_events(); + assert_eq!(listener.data.tasks_queue.snapshot().len(), 1); + assert_eq!(listener.data.tasks_queue.snapshot().pop_back(), Some(ServiceTask::GenerateServerKey( + Default::default(), Default::default(), Default::default(), 0))); + } + + #[test] + fn no_new_tasks_scheduled_when_server_key_generation_requested_and_request_belongs_to_other_key_server() { let server_key_id = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".parse().unwrap(); let mut contract = DummyServiceContract::default(); - contract.logs.push(vec![Default::default(), server_key_id, Default::default()]); - let listener = make_service_contract_listener(Some(Arc::new(contract)), None, None); + contract.logs.push(ServiceTask::GenerateServerKey(Default::default(), server_key_id, Default::default(), 0)); + let listener = make_service_contract_listener(Some(Arc::new(contract)), None, None, None, None); + assert_eq!(listener.data.tasks_queue.snapshot().len(), 0); + listener.process_service_contract_events(); + assert_eq!(listener.data.tasks_queue.snapshot().len(), 0); + } + + #[test] + fn generation_session_is_created_when_processing_generate_server_key_task() { + let cluster = Arc::new(DummyClusterClient::default()); + let listener = make_service_contract_listener(None, Some(cluster.clone()), None, None, None); + ServiceContractListener::process_service_task(&listener.data, ServiceTask::GenerateServerKey( + Default::default(), Default::default(), Default::default(), Default::default())).unwrap_err(); + assert_eq!(cluster.generation_requests_count.load(Ordering::Relaxed), 1); + } + + #[test] + fn server_key_generation_is_not_retried_if_tried_in_the_same_cycle() { + let mut contract = DummyServiceContract::default(); + contract.pending_requests.push((false, ServiceTask::GenerateServerKey(Default::default(), + Default::default(), Default::default(), Default::default()))); + let cluster = Arc::new(DummyClusterClient::default()); + let listener = make_service_contract_listener(Some(Arc::new(contract)), Some(cluster.clone()), None, None, None); + listener.data.retry_data.lock().affected_server_keys.insert(Default::default()); + ServiceContractListener::retry_pending_requests(&listener.data).unwrap(); + assert_eq!(cluster.generation_requests_count.load(Ordering::Relaxed), 0); + } + + // server key retrieval tests + + #[test] + fn server_key_retrieval_is_scheduled_when_requested() { + let mut contract = DummyServiceContract::default(); + contract.logs.push(ServiceTask::RetrieveServerKey(Default::default(), Default::default())); + let listener = make_service_contract_listener(Some(Arc::new(contract)), None, None, None, None); + assert_eq!(listener.data.tasks_queue.snapshot().len(), 0); + listener.process_service_contract_events(); assert_eq!(listener.data.tasks_queue.snapshot().len(), 1); + assert_eq!(listener.data.tasks_queue.snapshot().pop_back(), Some(ServiceTask::RetrieveServerKey( + Default::default(), Default::default()))); + } + + #[test] + fn server_key_retrieval_is_scheduled_when_requested_and_request_belongs_to_other_key_server() { + let server_key_id: ServerKeyId = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".parse().unwrap(); + let mut contract = DummyServiceContract::default(); + contract.logs.push(ServiceTask::RetrieveServerKey(Default::default(), server_key_id.clone())); + let listener = make_service_contract_listener(Some(Arc::new(contract)), None, None, None, None); + assert_eq!(listener.data.tasks_queue.snapshot().len(), 0); listener.process_service_contract_events(); assert_eq!(listener.data.tasks_queue.snapshot().len(), 1); + assert_eq!(listener.data.tasks_queue.snapshot().pop_back(), Some(ServiceTask::RetrieveServerKey( + Default::default(), server_key_id))); + } + + #[test] + fn server_key_is_retrieved_when_processing_retrieve_server_key_task() { + let contract = Arc::new(DummyServiceContract::default()); + let key_storage = create_non_empty_key_storage(false); + let listener = make_service_contract_listener(Some(contract.clone()), None, Some(key_storage), None, None); + ServiceContractListener::process_service_task(&listener.data, ServiceTask::RetrieveServerKey( + Default::default(), Default::default())).unwrap(); + assert_eq!(*contract.retrieved_server_keys.lock(), vec![(Default::default(), + KeyPair::from_secret("0000000000000000000000000000000000000000000000000000000000000001".parse().unwrap()).unwrap().public().clone(), 0)]); + } + + #[test] + fn server_key_retrieval_failure_is_reported_when_processing_retrieve_server_key_task_and_key_is_unknown() { + let contract = Arc::new(DummyServiceContract::default()); + let listener = make_service_contract_listener(Some(contract.clone()), None, None, None, None); + ServiceContractListener::process_service_task(&listener.data, ServiceTask::RetrieveServerKey( + Default::default(), Default::default())).unwrap(); + assert_eq!(*contract.server_keys_retrieval_failures.lock(), vec![Default::default()]); + } + + #[test] + fn server_key_retrieval_is_not_retried_if_tried_in_the_same_cycle() { + let mut contract = DummyServiceContract::default(); + contract.pending_requests.push((false, ServiceTask::RetrieveServerKey(Default::default(), Default::default()))); + let cluster = Arc::new(DummyClusterClient::default()); + let listener = make_service_contract_listener(Some(Arc::new(contract)), Some(cluster.clone()), None, None, None); + listener.data.retry_data.lock().affected_server_keys.insert(Default::default()); + ServiceContractListener::retry_pending_requests(&listener.data).unwrap(); + assert_eq!(cluster.generation_requests_count.load(Ordering::Relaxed), 0); } + // document key store tests + #[test] - fn server_key_restore_is_scheduled_when_requested_key_is_known() { + fn document_key_store_is_scheduled_when_requested() { let mut contract = DummyServiceContract::default(); - contract.logs.push(vec![Default::default(), Default::default(), Default::default()]); - let listener = make_service_contract_listener(Some(Arc::new(contract)), None, None); - listener.data.key_storage.insert(Default::default(), Default::default()).unwrap(); + contract.logs.push(ServiceTask::StoreDocumentKey(Default::default(), Default::default(), + Default::default(), Default::default(), Default::default())); + let listener = make_service_contract_listener(Some(Arc::new(contract)), None, None, None, None); + assert_eq!(listener.data.tasks_queue.snapshot().len(), 0); + listener.process_service_contract_events(); assert_eq!(listener.data.tasks_queue.snapshot().len(), 1); + assert_eq!(listener.data.tasks_queue.snapshot().pop_back(), Some(ServiceTask::StoreDocumentKey( + Default::default(), Default::default(), Default::default(), Default::default(), Default::default()))); + } + + #[test] + fn document_key_store_is_scheduled_when_requested_and_request_belongs_to_other_key_server() { + let server_key_id: ServerKeyId = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".parse().unwrap(); + let mut contract = DummyServiceContract::default(); + contract.logs.push(ServiceTask::StoreDocumentKey(Default::default(), server_key_id.clone(), + Default::default(), Default::default(), Default::default())); + let listener = make_service_contract_listener(Some(Arc::new(contract)), None, None, None, None); + assert_eq!(listener.data.tasks_queue.snapshot().len(), 0); listener.process_service_contract_events(); - assert_eq!(listener.data.tasks_queue.snapshot().len(), 2); - assert_eq!(listener.data.tasks_queue.snapshot().pop_back(), Some(ServiceTask::RestoreServerKey(Default::default()))); + assert_eq!(listener.data.tasks_queue.snapshot().len(), 1); + assert_eq!(listener.data.tasks_queue.snapshot().pop_back(), Some(ServiceTask::StoreDocumentKey( + Default::default(), server_key_id, Default::default(), Default::default(), Default::default()))); + } + + #[test] + fn document_key_is_stored_when_processing_store_document_key_task() { + let contract = Arc::new(DummyServiceContract::default()); + let key_storage = create_non_empty_key_storage(false); + let listener = make_service_contract_listener(Some(contract.clone()), None, Some(key_storage.clone()), None, None); + ServiceContractListener::process_service_task(&listener.data, ServiceTask::StoreDocumentKey( + Default::default(), Default::default(), Default::default(), Default::default(), Default::default())).unwrap(); + assert_eq!(*contract.stored_document_keys.lock(), vec![Default::default()]); + + let key_share = key_storage.get(&Default::default()).unwrap().unwrap(); + assert_eq!(key_share.common_point, Some(Default::default())); + assert_eq!(key_share.encrypted_point, Some(Default::default())); + } + + #[test] + fn document_key_store_failure_reported_when_no_server_key() { + let contract = Arc::new(DummyServiceContract::default()); + let listener = make_service_contract_listener(Some(contract.clone()), None, None, None, None); + ServiceContractListener::process_service_task(&listener.data, ServiceTask::StoreDocumentKey( + Default::default(), Default::default(), Default::default(), Default::default(), Default::default())).unwrap_err(); + assert_eq!(*contract.document_keys_store_failures.lock(), vec![Default::default()]); + } + + #[test] + fn document_key_store_failure_reported_when_document_key_already_set() { + let contract = Arc::new(DummyServiceContract::default()); + let key_storage = create_non_empty_key_storage(true); + let listener = make_service_contract_listener(Some(contract.clone()), None, Some(key_storage), None, None); + ServiceContractListener::process_service_task(&listener.data, ServiceTask::StoreDocumentKey( + Default::default(), Default::default(), Default::default(), Default::default(), Default::default())).unwrap_err(); + assert_eq!(*contract.document_keys_store_failures.lock(), vec![Default::default()]); + } + + #[test] + fn document_key_store_failure_reported_when_author_differs() { + let contract = Arc::new(DummyServiceContract::default()); + let key_storage = create_non_empty_key_storage(false); + let listener = make_service_contract_listener(Some(contract.clone()), None, Some(key_storage), None, None); + ServiceContractListener::process_service_task(&listener.data, ServiceTask::StoreDocumentKey( + Default::default(), Default::default(), 1.into(), Default::default(), Default::default())).unwrap_err(); + assert_eq!(*contract.document_keys_store_failures.lock(), vec![Default::default()]); } + // document key shadow common retrieval tests + #[test] - fn no_new_tasks_scheduled_when_wrong_number_of_topics_in_log() { + fn document_key_shadow_common_retrieval_is_scheduled_when_requested() { let mut contract = DummyServiceContract::default(); - contract.logs.push(vec![Default::default(), Default::default()]); - let listener = make_service_contract_listener(Some(Arc::new(contract)), None, None); + contract.logs.push(ServiceTask::RetrieveShadowDocumentKeyCommon(Default::default(), Default::default(), Default::default())); + let listener = make_service_contract_listener(Some(Arc::new(contract)), None, None, None, None); + assert_eq!(listener.data.tasks_queue.snapshot().len(), 0); + listener.process_service_contract_events(); assert_eq!(listener.data.tasks_queue.snapshot().len(), 1); + assert_eq!(listener.data.tasks_queue.snapshot().pop_back(), Some(ServiceTask::RetrieveShadowDocumentKeyCommon( + Default::default(), Default::default(), Default::default()))); + } + + #[test] + fn document_key_shadow_common_retrieval_is_scheduled_when_requested_and_request_belongs_to_other_key_server() { + let server_key_id: ServerKeyId = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".parse().unwrap(); + let mut contract = DummyServiceContract::default(); + contract.logs.push(ServiceTask::RetrieveShadowDocumentKeyCommon(Default::default(), server_key_id.clone(), Default::default())); + let listener = make_service_contract_listener(Some(Arc::new(contract)), None, None, None, None); + assert_eq!(listener.data.tasks_queue.snapshot().len(), 0); listener.process_service_contract_events(); assert_eq!(listener.data.tasks_queue.snapshot().len(), 1); + assert_eq!(listener.data.tasks_queue.snapshot().pop_back(), Some(ServiceTask::RetrieveShadowDocumentKeyCommon( + Default::default(), server_key_id, Default::default()))); } #[test] - fn generation_session_is_created_when_processing_generate_server_key_task() { - let key_server = Arc::new(DummyKeyServer::default()); - let listener = make_service_contract_listener(None, Some(key_server.clone()), None); - ServiceContractListener::process_service_task(&listener.data, ServiceTask::GenerateServerKey(Default::default(), Default::default())).unwrap_err(); - assert_eq!(key_server.generation_requests_count.load(Ordering::Relaxed), 1); + fn document_key_shadow_common_is_retrieved_when_processing_document_key_shadow_common_retrieval_task() { + let contract = Arc::new(DummyServiceContract::default()); + let key_storage = create_non_empty_key_storage(true); + let listener = make_service_contract_listener(Some(contract.clone()), None, Some(key_storage.clone()), None, None); + ServiceContractListener::process_service_task(&listener.data, ServiceTask::RetrieveShadowDocumentKeyCommon( + Default::default(), Default::default(), Default::default())).unwrap(); + assert_eq!(*contract.common_shadow_retrieved_document_keys.lock(), vec![(Default::default(), Default::default(), + Default::default(), 0)]); } #[test] - fn key_is_read_and_published_when_processing_restore_server_key_task() { + fn document_key_shadow_common_retrieval_failure_reported_when_access_denied() { + let acl_storage = DummyAclStorage::default(); + acl_storage.prohibit(Default::default(), Default::default()); let contract = Arc::new(DummyServiceContract::default()); - let key_storage = Arc::new(DummyKeyStorage::default()); - let mut key_share = DocumentKeyShare::default(); - key_share.public = KeyPair::from_secret("0000000000000000000000000000000000000000000000000000000000000001".parse().unwrap()).unwrap().public().clone(); - key_storage.insert(Default::default(), key_share.clone()).unwrap(); - let listener = make_service_contract_listener(Some(contract.clone()), None, Some(key_storage)); - ServiceContractListener::process_service_task(&listener.data, ServiceTask::RestoreServerKey(Default::default())).unwrap(); - assert_eq!(*contract.published_keys.lock(), vec![(Default::default(), key_share.public)]); + let key_storage = create_non_empty_key_storage(true); + let listener = make_service_contract_listener(Some(contract.clone()), None, Some(key_storage.clone()), Some(Arc::new(acl_storage)), None); + ServiceContractListener::process_service_task(&listener.data, ServiceTask::RetrieveShadowDocumentKeyCommon( + Default::default(), Default::default(), Default::default())).unwrap_err(); + assert_eq!(*contract.document_keys_shadow_retrieval_failures.lock(), vec![(Default::default(), Default::default())]); } #[test] - fn generation_is_not_retried_if_tried_in_the_same_cycle() { - let mut contract = DummyServiceContract::default(); - contract.pending_requests.push((false, ServiceTask::GenerateServerKey(Default::default(), Default::default()))); - let key_server = Arc::new(DummyKeyServer::default()); - let listener = make_service_contract_listener(Some(Arc::new(contract)), Some(key_server.clone()), None); - listener.data.retry_data.lock().generated_keys.insert(Default::default()); - ServiceContractListener::retry_pending_requests(&listener.data).unwrap(); - assert_eq!(key_server.generation_requests_count.load(Ordering::Relaxed), 0); + fn document_key_shadow_common_retrieval_failure_reported_when_no_server_key() { + let contract = Arc::new(DummyServiceContract::default()); + let listener = make_service_contract_listener(Some(contract.clone()), None, None, None, None); + ServiceContractListener::process_service_task(&listener.data, ServiceTask::RetrieveShadowDocumentKeyCommon( + Default::default(), Default::default(), Default::default())).unwrap_err(); + assert_eq!(*contract.document_keys_shadow_retrieval_failures.lock(), vec![(Default::default(), Default::default())]); + } + + #[test] + fn document_key_shadow_common_retrieval_failure_reported_when_no_document_key() { + let contract = Arc::new(DummyServiceContract::default()); + let key_storage = create_non_empty_key_storage(false); + let listener = make_service_contract_listener(Some(contract.clone()), None, Some(key_storage.clone()), None, None); + ServiceContractListener::process_service_task(&listener.data, ServiceTask::RetrieveShadowDocumentKeyCommon( + Default::default(), Default::default(), Default::default())).unwrap_err(); + assert_eq!(*contract.document_keys_shadow_retrieval_failures.lock(), vec![(Default::default(), Default::default())]); } } diff --git a/secret_store/src/listener/tasks_queue.rs b/secret_store/src/listener/tasks_queue.rs index e228d12cef81141d289fa7794aeb85ae545e81d8..934459940aeba00c5e07e5d2e0272bfa12871b25 100644 --- a/secret_store/src/listener/tasks_queue.rs +++ b/secret_store/src/listener/tasks_queue.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/secret_store/src/node_key_pair.rs b/secret_store/src/node_key_pair.rs index 75c840185684e7be8c245b4fb20f04fe0b4eaa1f..93cf285b2fd3c436a2c8b5bf8f1e8f7878f37d3b 100644 --- a/secret_store/src/node_key_pair.rs +++ b/secret_store/src/node_key_pair.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -15,7 +15,7 @@ // along with Parity. If not, see . use std::sync::Arc; -use ethcrypto::ecdh::agree; +use ethkey::crypto::ecdh::agree; use ethkey::{KeyPair, Public, Signature, Error as EthKeyError, sign, public_to_address}; use ethcore::account_provider::AccountProvider; use ethereum_types::{H256, Address}; @@ -54,7 +54,8 @@ impl NodeKeyPair for PlainNodeKeyPair { } fn compute_shared_key(&self, peer_public: &Public) -> Result { - agree(self.key_pair.secret(), peer_public).map_err(|e| EthKeyError::Custom(e.into())) + agree(self.key_pair.secret(), peer_public) + .map_err(|e| EthKeyError::Custom(e.to_string())) .and_then(KeyPair::from_secret) } } diff --git a/secret_store/src/serialization.rs b/secret_store/src/serialization.rs index 95f67909677c4ee0cc7d7dd0c51acd65c8af4e31..7ae5e8f2694a17ed4d7f591c1718ea723fb68132 100644 --- a/secret_store/src/serialization.rs +++ b/secret_store/src/serialization.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -22,7 +22,7 @@ use serde::de::{Visitor, Error as SerdeError}; use ethkey::{Public, Secret, Signature}; use ethereum_types::{H160, H256}; use bytes::Bytes; -use types::all::Requester; +use types::Requester; macro_rules! impl_bytes_deserialize { ($name: ident, $value: expr, true) => { @@ -117,7 +117,7 @@ impl_bytes!(SerializableSignature, Signature, false, ()); /// Serializable shadow decryption result. #[derive(Clone, Debug, Serialize, Deserialize)] pub struct SerializableEncryptedDocumentKeyShadow { - /// Decrypted secret point. It is partially decrypted if shadow decrpytion was requested. + /// Decrypted secret point. It is partially decrypted if shadow decryption was requested. pub decrypted_secret: SerializablePublic, /// Shared common point. pub common_point: SerializablePublic, diff --git a/secret_store/src/traits.rs b/secret_store/src/traits.rs index d486cfb10e10f204ec7da553b432475730353745..d92983fe8da7f48894792c20ec43e44049ced574 100644 --- a/secret_store/src/traits.rs +++ b/secret_store/src/traits.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -17,8 +17,8 @@ use std::collections::BTreeSet; use ethkey::{KeyPair, Signature, Error as EthKeyError}; use ethereum_types::{H256, Address}; -use types::all::{Error, Public, ServerKeyId, MessageHash, EncryptedMessageSignature, RequestSignature, EncryptedDocumentKey, - EncryptedDocumentKeyShadow, NodeId}; +use types::{Error, Public, ServerKeyId, MessageHash, EncryptedMessageSignature, RequestSignature, Requester, + EncryptedDocumentKey, EncryptedDocumentKeyShadow, NodeId}; /// Node key pair. pub trait NodeKeyPair: Send + Sync { @@ -36,34 +36,34 @@ pub trait NodeKeyPair: Send + Sync { pub trait ServerKeyGenerator { /// Generate new SK. /// `key_id` is the caller-provided identifier of generated SK. - /// `signature` is `key_id`, signed with caller public key. + /// `author` is the author of key entry. /// `threshold + 1` is the minimal number of nodes, required to restore private key. /// Result is a public portion of SK. - fn generate_key(&self, key_id: &ServerKeyId, signature: &RequestSignature, threshold: usize) -> Result; + fn generate_key(&self, key_id: &ServerKeyId, author: &Requester, threshold: usize) -> Result; } /// Document key (DK) server. pub trait DocumentKeyServer: ServerKeyGenerator { /// Store externally generated DK. /// `key_id` is identifier of previously generated SK. - /// `signature` is key_id, signed with caller public key. Caller must be the same as in the `generate_key` call. + /// `author` is the same author, that has created the server key. /// `common_point` is a result of `k * T` expression, where `T` is generation point and `k` is random scalar in EC field. /// `encrypted_document_key` is a result of `M + k * y` expression, where `M` is unencrypted document key (point on EC), /// `k` is the same scalar used in `common_point` calculation and `y` is previously generated public part of SK. - fn store_document_key(&self, key_id: &ServerKeyId, signature: &RequestSignature, common_point: Public, encrypted_document_key: Public) -> Result<(), Error>; + fn store_document_key(&self, key_id: &ServerKeyId, author: &Requester, common_point: Public, encrypted_document_key: Public) -> Result<(), Error>; /// Generate and store both SK and DK. This is a shortcut for consequent calls of `generate_key` and `store_document_key`. /// The only difference is that DK is generated by DocumentKeyServer (which might be considered unsafe). /// `key_id` is the caller-provided identifier of generated SK. - /// `signature` is `key_id`, signed with caller public key. + /// `author` is the author of server && document key entry. /// `threshold + 1` is the minimal number of nodes, required to restore private key. /// Result is a DK, encrypted with caller public key. - fn generate_document_key(&self, key_id: &ServerKeyId, signature: &RequestSignature, threshold: usize) -> Result; + fn generate_document_key(&self, key_id: &ServerKeyId, author: &Requester, threshold: usize) -> Result; /// Restore previously stored DK. /// DK is decrypted on the key server (which might be considered unsafe), and then encrypted with caller public key. /// `key_id` is identifier of previously generated SK. - /// `signature` is key_id, signed with caller public key. Caller must be on ACL for this function to succeed. + /// `requester` is the one who requests access to document key. Caller must be on ACL for this function to succeed. /// Result is a DK, encrypted with caller public key. - fn restore_document_key(&self, key_id: &ServerKeyId, signature: &RequestSignature) -> Result; + fn restore_document_key(&self, key_id: &ServerKeyId, requester: &Requester) -> Result; /// Restore previously stored DK. /// To decrypt DK on client: /// 1) use requestor secret key to decrypt secret coefficients from result.decrypt_shadows @@ -71,30 +71,30 @@ pub trait DocumentKeyServer: ServerKeyGenerator { /// 3) calculate decrypt_shadow_point: decrypt_shadows_sum * result.common_point /// 4) calculate decrypted_secret: result.decrypted_secret + decrypt_shadow_point /// Result is a DK shadow. - fn restore_document_key_shadow(&self, key_id: &ServerKeyId, signature: &RequestSignature) -> Result; + fn restore_document_key_shadow(&self, key_id: &ServerKeyId, requester: &Requester) -> Result; } /// Message signer. pub trait MessageSigner: ServerKeyGenerator { /// Generate Schnorr signature for message with previously generated SK. /// `key_id` is the caller-provided identifier of generated SK. - /// `signature` is `key_id`, signed with caller public key. + /// `requester` is the one who requests access to server key private. /// `message` is the message to be signed. /// Result is a signed message, encrypted with caller public key. - fn sign_message_schnorr(&self, key_id: &ServerKeyId, signature: &RequestSignature, message: MessageHash) -> Result; + fn sign_message_schnorr(&self, key_id: &ServerKeyId, requester: &Requester, message: MessageHash) -> Result; /// Generate ECDSA signature for message with previously generated SK. /// WARNING: only possible when SK was generated using t <= 2 * N. /// `key_id` is the caller-provided identifier of generated SK. /// `signature` is `key_id`, signed with caller public key. /// `message` is the message to be signed. /// Result is a signed message, encrypted with caller public key. - fn sign_message_ecdsa(&self, key_id: &ServerKeyId, signature: &RequestSignature, message: MessageHash) -> Result; + fn sign_message_ecdsa(&self, key_id: &ServerKeyId, signature: &Requester, message: MessageHash) -> Result; } /// Administrative sessions server. pub trait AdminSessionsServer { /// Change servers set so that nodes in new_servers_set became owners of shares for all keys. - /// And old nodes (i.e. cluste nodes except new_servers_set) have clear databases. + /// And old nodes (i.e. cluster nodes except new_servers_set) have clear databases. /// WARNING: newly generated keys will be distributed among all cluster nodes. So this session /// must be followed with cluster nodes change (either via contract, or config files). fn change_servers_set(&self, old_set_signature: RequestSignature, new_set_signature: RequestSignature, new_servers_set: BTreeSet) -> Result<(), Error>; diff --git a/secret_store/src/trusted_client.rs b/secret_store/src/trusted_client.rs index 8a0f83d44f32d00b34c8e70323789473b3d1328f..24db21460005be01db2aacffaae5b6ff398b2da1 100644 --- a/secret_store/src/trusted_client.rs +++ b/secret_store/src/trusted_client.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -15,24 +15,36 @@ // along with Parity. If not, see . use std::sync::{Arc, Weak}; -use ethcore::client::{Client, BlockChainClient, ChainInfo}; -use ethsync::SyncProvider; +use bytes::Bytes; +use ethereum_types::Address; +use ethcore::client::{Client, BlockChainClient, ChainInfo, Nonce, BlockId, RegistryInfo}; +use ethcore::miner::{Miner, MinerService}; +use sync::SyncProvider; +use transaction::{Transaction, SignedTransaction, Action}; +use helpers::{get_confirmed_block_hash, REQUEST_CONFIRMATIONS_REQUIRED}; +use {Error, NodeKeyPair, ContractAddress}; #[derive(Clone)] /// 'Trusted' client weak reference. pub struct TrustedClient { + /// This key server node key pair. + self_key_pair: Arc, /// Blockchain client. client: Weak, /// Sync provider. sync: Weak, + /// Miner service. + miner: Weak, } impl TrustedClient { /// Create new trusted client. - pub fn new(client: Arc, sync: Arc) -> Self { + pub fn new(self_key_pair: Arc, client: Arc, sync: Arc, miner: Arc) -> Self { TrustedClient { + self_key_pair: self_key_pair, client: Arc::downgrade(&client), sync: Arc::downgrade(&sync), + miner: Arc::downgrade(&miner), } } @@ -54,4 +66,37 @@ impl TrustedClient { pub fn get_untrusted(&self) -> Option> { self.client.upgrade() } + + /// Transact contract. + pub fn transact_contract(&self, contract: Address, tx_data: Bytes) -> Result<(), Error> { + let client = self.client.upgrade().ok_or_else(|| Error::Internal("cannot submit tx when client is offline".into()))?; + let miner = self.miner.upgrade().ok_or_else(|| Error::Internal("cannot submit tx when miner is offline".into()))?; + let engine = client.engine(); + let transaction = Transaction { + nonce: client.latest_nonce(&self.self_key_pair.address()), + action: Action::Call(contract), + gas: miner.authoring_params().gas_range_target.0, + gas_price: miner.sensible_gas_price(), + value: Default::default(), + data: tx_data, + }; + let chain_id = engine.signing_chain_id(&client.latest_env_info()); + let signature = self.self_key_pair.sign(&transaction.hash(chain_id))?; + let signed = SignedTransaction::new(transaction.with_signature(signature, chain_id))?; + miner.import_own_transaction(&*client, signed.into()) + .map_err(|e| Error::Internal(format!("failed to import tx: {}", e))) + } + + /// Read contract address. If address source is registry, address only returned if current client state is + /// trusted. Address from registry is read from registry from block latest block with + /// REQUEST_CONFIRMATIONS_REQUIRED confirmations. + pub fn read_contract_address(&self, registry_name: String, address: &ContractAddress) -> Option
{ + match *address { + ContractAddress::Address(ref address) => Some(address.clone()), + ContractAddress::Registry => self.get().and_then(|client| + get_confirmed_block_hash(&*client, REQUEST_CONFIRMATIONS_REQUIRED) + .and_then(|block| client.registry_address(registry_name, BlockId::Hash(block))) + ), + } + } } diff --git a/secret_store/src/types/all.rs b/secret_store/src/types/all.rs index 9c6f4d1721c18e77631576be66f188c8566756a5..feca4141f084b0c79cde7db4d9b620c99737cde5 100644 --- a/secret_store/src/types/all.rs +++ b/secret_store/src/types/all.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -14,11 +14,9 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use std::fmt; use std::collections::BTreeMap; -use serde_json; -use {ethkey, kvdb, bytes, ethereum_types, key_server_cluster}; +use {ethkey, bytes, ethereum_types}; /// Node id. pub type NodeId = ethkey::Public; @@ -35,25 +33,6 @@ pub type RequestSignature = ethkey::Signature; /// Public key type. pub use ethkey::Public; -/// Secret store error -#[derive(Debug, PartialEq)] -pub enum Error { - /// Bad signature is passed - BadSignature, - /// Access to resource is denied - AccessDenied, - /// Requested document not found - DocumentNotFound, - /// Hyper error - Hyper(String), - /// Serialization/deserialization error - Serde(String), - /// Database-related error - Database(String), - /// Internal error - Internal(String), -} - /// Secret store configuration #[derive(Debug, Clone)] pub struct NodeAddress { @@ -77,12 +56,18 @@ pub enum ContractAddress { pub struct ServiceConfiguration { /// HTTP listener address. If None, HTTP API is disabled. pub listener_address: Option, - /// Service contract address. If None, service contract API is disabled. + /// Service contract address. pub service_contract_address: Option, - /// Is ACL check enabled. If false, everyone has access to all keys. Useful for tests only. - pub acl_check_enabled: bool, - /// Data directory path for secret store - pub data_path: String, + /// Server key generation service contract address. + pub service_contract_srv_gen_address: Option, + /// Server key retrieval service contract address. + pub service_contract_srv_retr_address: Option, + /// Document key store service contract address. + pub service_contract_doc_store_address: Option, + /// Document key shadow retrieval service contract address. + pub service_contract_doc_sretr_address: Option, + /// ACL check contract address. If None, everyone has access to all keys. Useful for tests only. + pub acl_check_contract_address: Option, /// Cluster configuration. pub cluster_config: ClusterConfiguration, } @@ -96,6 +81,8 @@ pub struct ClusterConfiguration { pub listener_address: NodeAddress, /// All cluster nodes addresses. pub nodes: BTreeMap, + /// Key Server Set contract address. If None, servers from 'nodes' map are used. + pub key_server_set_contract_address: Option, /// Allow outbound connections to 'higher' nodes. /// This is useful for tests, but slower a bit for production. pub allow_connecting_to_higher_nodes: bool, @@ -109,7 +96,7 @@ pub struct ClusterConfiguration { /// Shadow decryption result. #[derive(Clone, Debug, PartialEq)] pub struct EncryptedDocumentKeyShadow { - /// Decrypted secret point. It is partially decrypted if shadow decrpytion was requested. + /// Decrypted secret point. It is partially decrypted if shadow decryption was requested. pub decrypted_secret: ethkey::Public, /// Shared common point. pub common_point: Option, @@ -128,55 +115,6 @@ pub enum Requester { Address(ethereum_types::Address), } -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - match *self { - Error::BadSignature => write!(f, "Bad signature"), - Error::AccessDenied => write!(f, "Access dened"), - Error::DocumentNotFound => write!(f, "Document not found"), - Error::Hyper(ref msg) => write!(f, "Hyper error: {}", msg), - Error::Serde(ref msg) => write!(f, "Serialization error: {}", msg), - Error::Database(ref msg) => write!(f, "Database error: {}", msg), - Error::Internal(ref msg) => write!(f, "Internal error: {}", msg), - } - } -} - -impl From for Error { - fn from(err: serde_json::Error) -> Self { - Error::Serde(err.to_string()) - } -} - -impl From for Error { - fn from(err: ethkey::Error) -> Self { - Error::Internal(err.into()) - } -} - -impl From for Error { - fn from(err: kvdb::Error) -> Self { - Error::Database(err.to_string()) - } -} - -impl From for Error { - fn from(err: key_server_cluster::Error) -> Self { - match err { - key_server_cluster::Error::ConsensusUnreachable - | key_server_cluster::Error::AccessDenied => Error::AccessDenied, - key_server_cluster::Error::MissingKeyShare => Error::DocumentNotFound, - _ => Error::Internal(err.into()), - } - } -} - -impl Into for Error { - fn into(self) -> String { - format!("{}", self) - } -} - impl Default for Requester { fn default() -> Self { Requester::Signature(Default::default()) @@ -184,16 +122,18 @@ impl Default for Requester { } impl Requester { - pub fn public(&self, server_key_id: &ServerKeyId) -> Option { + pub fn public(&self, server_key_id: &ServerKeyId) -> Result { match *self { - Requester::Signature(ref signature) => ethkey::recover(signature, server_key_id).ok(), - Requester::Public(ref public) => Some(public.clone()), - Requester::Address(_) => None, + Requester::Signature(ref signature) => ethkey::recover(signature, server_key_id) + .map_err(|e| format!("bad signature: {}", e)), + Requester::Public(ref public) => Ok(public.clone()), + Requester::Address(_) => Err("cannot recover public from address".into()), } } - pub fn address(&self, server_key_id: &ServerKeyId) -> Option { - self.public(server_key_id).map(|p| ethkey::public_to_address(&p)) + pub fn address(&self, server_key_id: &ServerKeyId) -> Result { + self.public(server_key_id) + .map(|p| ethkey::public_to_address(&p)) } } @@ -202,3 +142,15 @@ impl From for Requester { Requester::Signature(signature) } } + +impl From for Requester { + fn from(public: ethereum_types::Public) -> Requester { + Requester::Public(public) + } +} + +impl From for Requester { + fn from(address: ethereum_types::Address) -> Requester { + Requester::Address(address) + } +} diff --git a/secret_store/src/types/error.rs b/secret_store/src/types/error.rs new file mode 100644 index 0000000000000000000000000000000000000000..74e6bb9e3cd16f80926d6d91e66b2527b8cdc63b --- /dev/null +++ b/secret_store/src/types/error.rs @@ -0,0 +1,205 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use std::fmt; +use std::net; +use std::io::Error as IoError; + +use {ethkey, crypto, kvdb}; + +/// Secret store error. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub enum Error { + /// Invalid node address has been passed. + InvalidNodeAddress, + /// Invalid node id has been passed. + InvalidNodeId, + /// Session with the given id already exists. + DuplicateSessionId, + /// No active session with given id. + NoActiveSessionWithId, + /// Invalid threshold value has been passed. + /// Threshold value must be in [0; n - 1], where n is a number of nodes participating in the encryption. + NotEnoughNodesForThreshold, + /// Current state of encryption/decryption session does not allow to proceed request. + /// Reschedule this request for later processing. + TooEarlyForRequest, + /// Current state of encryption/decryption session does not allow to proceed request. + /// This means that either there is some comm-failure or node is misbehaving/cheating. + InvalidStateForRequest, + /// Request cannot be sent/received from this node. + InvalidNodeForRequest, + /// Message or some data in the message was recognized as invalid. + /// This means that node is misbehaving/cheating. + InvalidMessage, + /// Message version is not supported. + InvalidMessageVersion, + /// Message is invalid because of replay-attack protection. + ReplayProtection, + /// Connection to node, required for this session is not established. + NodeDisconnected, + /// Server key with this ID is already generated. + ServerKeyAlreadyGenerated, + /// Server key with this ID is not yet generated. + ServerKeyIsNotFound, + /// Document key with this ID is already stored. + DocumentKeyAlreadyStored, + /// Document key with this ID is not yet stored. + DocumentKeyIsNotFound, + /// Consensus is temporary unreachable. Means that something is currently blocking us from either forming + /// consensus group (like disconnecting from too many nodes, which are AGREE to participate in consensus) + /// or from rejecting request (disconnecting from AccessDenied-nodes). + ConsensusTemporaryUnreachable, + /// Consensus is unreachable. It doesn't mean that it will ALWAYS remain unreachable, but right NOW we have + /// enough nodes confirmed that they do not want to be a part of consensus. Example: we're connected to 10 + /// of 100 nodes. Key threshold is 6 (i.e. 7 nodes are required for consensus). 4 nodes are responding with + /// reject => consensus is considered unreachable, even though another 90 nodes still can respond with OK. + ConsensusUnreachable, + /// Acl storage error. + AccessDenied, + /// Can't start session, because exclusive session is active. + ExclusiveSessionActive, + /// Can't start exclusive session, because there are other active sessions. + HasActiveSessions, + /// Insufficient requester data. + InsufficientRequesterData(String), + /// Cryptographic error. + EthKey(String), + /// I/O error has occurred. + Io(String), + /// Deserialization error has occurred. + Serde(String), + /// Hyper error. + Hyper(String), + /// Database-related error. + Database(String), + /// Internal error. + Internal(String), +} + +impl Error { + /// Is this a fatal error? Non-fatal means that it is possible to replay the same request with a non-zero + /// chance to success. I.e. the error is not about request itself (or current environment factors that + /// are affecting request processing), but about current SecretStore state. + pub fn is_non_fatal(&self) -> bool { + match *self { + // non-fatal errors: + + // session start errors => restarting session is a solution + Error::DuplicateSessionId | Error::NoActiveSessionWithId | + // unexpected message errors => restarting session/excluding node is a solution + Error::TooEarlyForRequest | Error::InvalidStateForRequest | Error::InvalidNodeForRequest | + // invalid message errors => restarting/updating/excluding node is a solution + Error::InvalidMessage | Error::InvalidMessageVersion | Error::ReplayProtection | + // connectivity problems => waiting for reconnect && restarting session is a solution + Error::NodeDisconnected | + // temporary (?) consensus problems, related to other non-fatal errors => restarting is probably (!) a solution + Error::ConsensusTemporaryUnreachable | + // exclusive session errors => waiting && restarting is a solution + Error::ExclusiveSessionActive | Error::HasActiveSessions => true, + + // fatal errors: + + // config-related errors + Error::InvalidNodeAddress | Error::InvalidNodeId | + // wrong session input params errors + Error::NotEnoughNodesForThreshold | Error::ServerKeyAlreadyGenerated | Error::ServerKeyIsNotFound | + Error::DocumentKeyAlreadyStored | Error::DocumentKeyIsNotFound | Error::InsufficientRequesterData(_) | + // access denied/consensus error + Error::AccessDenied | Error::ConsensusUnreachable | + // indeterminate internal errors, which could be either fatal (db failure, invalid request), or not (network error), + // but we still consider these errors as fatal + Error::EthKey(_) | Error::Serde(_) | Error::Hyper(_) | Error::Database(_) | Error::Internal(_) | Error::Io(_) => false, + } + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + match *self { + Error::InvalidNodeAddress => write!(f, "invalid node address has been passed"), + Error::InvalidNodeId => write!(f, "invalid node id has been passed"), + Error::DuplicateSessionId => write!(f, "session with the same id is already registered"), + Error::NoActiveSessionWithId => write!(f, "no active session with given id"), + Error::NotEnoughNodesForThreshold => write!(f, "not enough nodes for passed threshold"), + Error::TooEarlyForRequest => write!(f, "session is not yet ready to process this request"), + Error::InvalidStateForRequest => write!(f, "session is in invalid state for processing this request"), + Error::InvalidNodeForRequest => write!(f, "invalid node for this request"), + Error::InvalidMessage => write!(f, "invalid message is received"), + Error::InvalidMessageVersion => write!(f, "unsupported message is received"), + Error::ReplayProtection => write!(f, "replay message is received"), + Error::NodeDisconnected => write!(f, "node required for this operation is currently disconnected"), + Error::ServerKeyAlreadyGenerated => write!(f, "Server key with this ID is already generated"), + Error::ServerKeyIsNotFound => write!(f, "Server key with this ID is not found"), + Error::DocumentKeyAlreadyStored => write!(f, "Document key with this ID is already stored"), + Error::DocumentKeyIsNotFound => write!(f, "Document key with this ID is not found"), + Error::ConsensusUnreachable => write!(f, "Consensus unreachable"), + Error::ConsensusTemporaryUnreachable => write!(f, "Consensus temporary unreachable"), + Error::AccessDenied => write!(f, "Access denied"), + Error::ExclusiveSessionActive => write!(f, "Exclusive session active"), + Error::HasActiveSessions => write!(f, "Unable to start exclusive session"), + Error::InsufficientRequesterData(ref e) => write!(f, "Insufficient requester data: {}", e), + Error::EthKey(ref e) => write!(f, "cryptographic error {}", e), + Error::Hyper(ref msg) => write!(f, "Hyper error: {}", msg), + Error::Serde(ref msg) => write!(f, "Serialization error: {}", msg), + Error::Database(ref msg) => write!(f, "Database error: {}", msg), + Error::Internal(ref msg) => write!(f, "Internal error: {}", msg), + Error::Io(ref msg) => write!(f, "IO error: {}", msg), + } + } +} + +impl From for Error { + fn from(err: ethkey::Error) -> Self { + Error::EthKey(err.into()) + } +} + +impl From for Error { + fn from(err: ethkey::crypto::Error) -> Self { + Error::EthKey(err.to_string()) + } +} + +impl From for Error { + fn from(err: kvdb::Error) -> Self { + Error::Database(err.to_string()) + } +} + +impl From for Error { + fn from(err: crypto::Error) -> Self { + Error::EthKey(err.to_string()) + } +} + +impl From for Error { + fn from(err: IoError) -> Self { + Error::Io(err.to_string()) + } +} + +impl Into for Error { + fn into(self) -> String { + format!("{}", self) + } +} + +impl From for Error { + fn from(err: net::AddrParseError) -> Error { + Error::Internal(err.to_string()) + } +} diff --git a/secret_store/src/types/mod.rs b/secret_store/src/types/mod.rs index dc5bd3d8a0898a621b8a131d522e22abe4de718f..443f4acb3a5725c12f2e9d1b7438fa9073d756cc 100644 --- a/secret_store/src/types/mod.rs +++ b/secret_store/src/types/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -16,4 +16,8 @@ //! Types used in the public api -pub mod all; +mod all; +mod error; + +pub use self::all::*; +pub use self::error::*; diff --git a/sync/Cargo.toml b/sync/Cargo.toml deleted file mode 100644 index e82dd710de61622eb7b97268ae1c90b8a1432458..0000000000000000000000000000000000000000 --- a/sync/Cargo.toml +++ /dev/null @@ -1,36 +0,0 @@ -[package] -description = "Ethcore blockchain sync" -name = "ethsync" -version = "1.11.0" -license = "GPL-3.0" -authors = ["Parity Technologies "] - -[lib] - -[dependencies] -ethcore-bytes = { path = "../util/bytes" } -ethcore-network = { path = "../util/network" } -ethcore-network-devp2p = { path = "../util/network-devp2p" } -ethcore-io = { path = "../util/io" } -ethcore-light = { path = "../ethcore/light" } -ethcore-transaction = { path = "../ethcore/transaction" } -ethcore = { path = "../ethcore" } -ethereum-types = "0.2" -plain_hasher = { path = "../util/plain_hasher" } -rlp = { path = "../util/rlp" } -keccak-hash = { path = "../util/hash" } -triehash = { path = "../util/triehash" } -kvdb = { path = "../util/kvdb" } -macros = { path = "../util/macros" } -log = "0.3" -env_logger = "0.4" -rand = "0.4" -heapsize = "0.4" -semver = "0.6" -smallvec = { version = "0.4", features = ["heapsizeof"] } -parking_lot = "0.5" -ipnetwork = "0.12.6" - -[dev-dependencies] -ethkey = { path = "../ethkey" } -kvdb-memorydb = { path = "../util/kvdb-memorydb" } diff --git a/sync/src/chain.rs b/sync/src/chain.rs deleted file mode 100644 index a4d72ac96be4752f8be2c94a8488eff4b3ab85bf..0000000000000000000000000000000000000000 --- a/sync/src/chain.rs +++ /dev/null @@ -1,3022 +0,0 @@ -// Copyright 2015-2017 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 . - -/// `BlockChain` synchronization strategy. -/// Syncs to peers and keeps up to date. -/// This implementation uses ethereum protocol v63 -/// -/// Syncing strategy summary. -/// Split the chain into ranges of N blocks each. Download ranges sequentially. Split each range into subchains of M blocks. Download subchains in parallel. -/// State. -/// Sync state consists of the following data: -/// - s: State enum which can be one of the following values: `ChainHead`, `Blocks`, `Idle` -/// - H: A set of downloaded block headers -/// - B: A set of downloaded block bodies -/// - S: Set of block subchain start block hashes to download. -/// - l: Last imported / common block hash -/// - P: A set of connected peers. For each peer we maintain its last known total difficulty and starting block hash being requested if any. -/// General behaviour. -/// We start with all sets empty, l is set to the best block in the block chain, s is set to `ChainHead`. -/// If at any moment a bad block is reported by the block queue, we set s to `ChainHead`, reset l to the best block in the block chain and clear H, B and S. -/// If at any moment P becomes empty, we set s to `ChainHead`, and clear H, B and S. -/// -/// Workflow for `ChainHead` state. -/// In this state we try to get subchain headers with a single `GetBlockHeaders` request. -/// On `NewPeer` / On `Restart`: -/// If peer's total difficulty is higher and there are less than 5 peers downloading, request N/M headers with interval M+1 starting from l -/// On `BlockHeaders(R)`: -/// If R is empty: -/// If l is equal to genesis block hash or l is more than 1000 blocks behind our best hash: -/// Remove current peer from P. set l to the best block in the block chain. Select peer with maximum total difficulty from P and restart. -/// Else -/// Set l to l’s parent and restart. -/// Else if we already have all the headers in the block chain or the block queue: -/// Set s to `Idle`, -/// Else -/// Set S to R, set s to `Blocks`. -/// -/// All other messages are ignored. -/// -/// Workflow for `Blocks` state. -/// In this state we download block headers and bodies from multiple peers. -/// On `NewPeer` / On `Restart`: -/// For all idle peers: -/// Find a set of 256 or less block hashes in H which are not in B and not being downloaded by other peers. If the set is not empty: -/// Request block bodies for the hashes in the set. -/// Else -/// Find an element in S which is not being downloaded by other peers. If found: Request M headers starting from the element. -/// -/// On `BlockHeaders(R)`: -/// If R is empty remove current peer from P and restart. -/// Validate received headers: -/// For each header find a parent in H or R or the blockchain. Restart if there is a block with unknown parent. -/// Find at least one header from the received list in S. Restart if there is none. -/// Go to `CollectBlocks`. -/// -/// On `BlockBodies(R)`: -/// If R is empty remove current peer from P and restart. -/// Add bodies with a matching header in H to B. -/// Go to `CollectBlocks`. -/// -/// `CollectBlocks`: -/// Find a chain of blocks C in H starting from h where h’s parent equals to l. The chain ends with the first block which does not have a body in B. -/// Add all blocks from the chain to the block queue. Remove them from H and B. Set l to the hash of the last block from C. -/// Update and merge subchain heads in S. For each h in S find a chain of blocks in B starting from h. Remove h from S. if the chain does not include an element from S add the end of the chain to S. -/// If H is empty and S contains a single element set s to `ChainHead`. -/// Restart. -/// -/// All other messages are ignored. -/// Workflow for Idle state. -/// On `NewBlock`: -/// Import the block. If the block is unknown set s to `ChainHead` and restart. -/// On `NewHashes`: -/// Set s to `ChainHead` and restart. -/// -/// All other messages are ignored. -/// - -use std::collections::{HashSet, HashMap}; -use std::cmp; -use std::time::Instant; -use hash::keccak; -use heapsize::HeapSizeOf; -use ethereum_types::{H256, U256}; -use plain_hasher::H256FastMap; -use parking_lot::RwLock; -use bytes::Bytes; -use rlp::*; -use network::{self, PeerId, PacketId}; -use ethcore::header::{BlockNumber, Header as BlockHeader}; -use ethcore::client::{BlockChainClient, BlockStatus, BlockId, BlockChainInfo, BlockImportError, BlockQueueInfo}; -use ethcore::error::*; -use ethcore::snapshot::{ManifestData, RestorationStatus}; -use transaction::PendingTransaction; -use sync_io::SyncIo; -use super::SyncConfig; -use block_sync::{BlockDownloader, BlockRequest, BlockDownloaderImportError as DownloaderImportError, DownloadAction}; -use rand::Rng; -use snapshot::{Snapshot, ChunkType}; -use api::{EthProtocolInfo as PeerInfoDigest, WARP_SYNC_PROTOCOL_ID}; -use transactions_stats::{TransactionsStats, Stats as TransactionStats}; - -known_heap_size!(0, PeerInfo); - -type PacketDecodeError = DecoderError; - -const PROTOCOL_VERSION_63: u8 = 63; -const PROTOCOL_VERSION_62: u8 = 62; -const PROTOCOL_VERSION_1: u8 = 1; -const PROTOCOL_VERSION_2: u8 = 2; -const MAX_BODIES_TO_SEND: usize = 256; -const MAX_HEADERS_TO_SEND: usize = 512; -const MAX_NODE_DATA_TO_SEND: usize = 1024; -const MAX_RECEIPTS_TO_SEND: usize = 1024; -const MAX_RECEIPTS_HEADERS_TO_SEND: usize = 256; -const MIN_PEERS_PROPAGATION: usize = 4; -const MAX_PEERS_PROPAGATION: usize = 128; -const MAX_PEER_LAG_PROPAGATION: BlockNumber = 20; -const MAX_NEW_HASHES: usize = 64; -const MAX_NEW_BLOCK_AGE: BlockNumber = 20; -const MAX_TRANSACTION_SIZE: usize = 300*1024; -// maximal packet size with transactions (cannot be greater than 16MB - protocol limitation). -const MAX_TRANSACTION_PACKET_SIZE: usize = 8 * 1024 * 1024; -// Maximal number of transactions in sent in single packet. -const MAX_TRANSACTIONS_TO_PROPAGATE: usize = 64; -// Min number of blocks to be behind for a snapshot sync -const SNAPSHOT_RESTORE_THRESHOLD: BlockNumber = 30000; -const SNAPSHOT_MIN_PEERS: usize = 3; - -const STATUS_PACKET: u8 = 0x00; -const NEW_BLOCK_HASHES_PACKET: u8 = 0x01; -const TRANSACTIONS_PACKET: u8 = 0x02; -const GET_BLOCK_HEADERS_PACKET: u8 = 0x03; -const BLOCK_HEADERS_PACKET: u8 = 0x04; -const GET_BLOCK_BODIES_PACKET: u8 = 0x05; -const BLOCK_BODIES_PACKET: u8 = 0x06; -const NEW_BLOCK_PACKET: u8 = 0x07; - -const GET_NODE_DATA_PACKET: u8 = 0x0d; -const NODE_DATA_PACKET: u8 = 0x0e; -const GET_RECEIPTS_PACKET: u8 = 0x0f; -const RECEIPTS_PACKET: u8 = 0x10; - -pub const ETH_PACKET_COUNT: u8 = 0x11; - -const GET_SNAPSHOT_MANIFEST_PACKET: u8 = 0x11; -const SNAPSHOT_MANIFEST_PACKET: u8 = 0x12; -const GET_SNAPSHOT_DATA_PACKET: u8 = 0x13; -const SNAPSHOT_DATA_PACKET: u8 = 0x14; -const CONSENSUS_DATA_PACKET: u8 = 0x15; - -pub const SNAPSHOT_SYNC_PACKET_COUNT: u8 = 0x16; - -const MAX_SNAPSHOT_CHUNKS_DOWNLOAD_AHEAD: usize = 3; - -const WAIT_PEERS_TIMEOUT_SEC: u64 = 5; -const STATUS_TIMEOUT_SEC: u64 = 5; -const HEADERS_TIMEOUT_SEC: u64 = 15; -const BODIES_TIMEOUT_SEC: u64 = 20; -const RECEIPTS_TIMEOUT_SEC: u64 = 10; -const FORK_HEADER_TIMEOUT_SEC: u64 = 3; -const SNAPSHOT_MANIFEST_TIMEOUT_SEC: u64 = 5; -const SNAPSHOT_DATA_TIMEOUT_SEC: u64 = 120; - -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -/// Sync state -pub enum SyncState { - /// Collecting enough peers to start syncing. - WaitingPeers, - /// Waiting for snapshot manifest download - SnapshotManifest, - /// Downloading snapshot data - SnapshotData, - /// Waiting for snapshot restoration progress. - SnapshotWaiting, - /// Downloading new blocks - Blocks, - /// Initial chain sync complete. Waiting for new packets - Idle, - /// Block downloading paused. Waiting for block queue to process blocks and free some space - Waiting, - /// Downloading blocks learned from `NewHashes` packet - NewBlocks, -} - -/// Syncing status and statistics -#[derive(Clone, Copy)] -pub struct SyncStatus { - /// State - pub state: SyncState, - /// Syncing protocol version. That's the maximum protocol version we connect to. - pub protocol_version: u8, - /// The underlying p2p network version. - pub network_id: u64, - /// `BlockChain` height for the moment the sync started. - pub start_block_number: BlockNumber, - /// Last fully downloaded and imported block number (if any). - pub last_imported_block_number: Option, - /// Highest block number in the download queue (if any). - pub highest_block_number: Option, - /// Total number of blocks for the sync process. - pub blocks_total: BlockNumber, - /// Number of blocks downloaded so far. - pub blocks_received: BlockNumber, - /// Total number of connected peers - pub num_peers: usize, - /// Total number of active peers. - pub num_active_peers: usize, - /// Heap memory used in bytes. - pub mem_used: usize, - /// Snapshot chunks - pub num_snapshot_chunks: usize, - /// Snapshot chunks downloaded - pub snapshot_chunks_done: usize, - /// Last fully downloaded and imported ancient block number (if any). - pub last_imported_old_block_number: Option, -} - -impl SyncStatus { - /// Indicates if snapshot download is in progress - pub fn is_snapshot_syncing(&self) -> bool { - self.state == SyncState::SnapshotManifest - || self.state == SyncState::SnapshotData - || self.state == SyncState::SnapshotWaiting - } - - /// Returns max no of peers to display in informants - pub fn current_max_peers(&self, min_peers: u32, max_peers: u32) -> u32 { - if self.num_peers as u32 > min_peers { - max_peers - } else { - min_peers - } - } - - /// Is it doing a major sync? - pub fn is_syncing(&self, queue_info: BlockQueueInfo) -> bool { - let is_syncing_state = match self.state { SyncState::Idle | SyncState::NewBlocks => false, _ => true }; - let is_verifying = queue_info.unverified_queue_size + queue_info.verified_queue_size > 3; - is_verifying || is_syncing_state - } -} - -#[derive(PartialEq, Eq, Debug, Clone)] -/// Peer data type requested -enum PeerAsking { - Nothing, - ForkHeader, - BlockHeaders, - BlockBodies, - BlockReceipts, - SnapshotManifest, - SnapshotData, -} - -#[derive(PartialEq, Eq, Debug, Clone, Copy)] -/// Block downloader channel. -enum BlockSet { - /// New blocks better than out best blocks - NewBlocks, - /// Missing old blocks - OldBlocks, -} -#[derive(Clone, Eq, PartialEq)] -enum ForkConfirmation { - /// Fork block confirmation pending. - Unconfirmed, - /// Peers chain is too short to confirm the fork. - TooShort, - /// Fork is confirmed. - Confirmed, -} - -#[derive(Clone)] -/// Syncing peer information -struct PeerInfo { - /// eth protocol version - protocol_version: u8, - /// Peer chain genesis hash - genesis: H256, - /// Peer network id - network_id: u64, - /// Peer best block hash - latest_hash: H256, - /// Peer total difficulty if known - difficulty: Option, - /// Type of data currenty being requested from peer. - asking: PeerAsking, - /// A set of block numbers being requested - asking_blocks: Vec, - /// Holds requested header hash if currently requesting block header by hash - asking_hash: Option, - /// Holds requested snapshot chunk hash if any. - asking_snapshot_data: Option, - /// Request timestamp - ask_time: Instant, - /// Holds a set of transactions recently sent to this peer to avoid spamming. - last_sent_transactions: HashSet, - /// Pending request is expired and result should be ignored - expired: bool, - /// Peer fork confirmation status - confirmation: ForkConfirmation, - /// Best snapshot hash - snapshot_hash: Option, - /// Best snapshot block number - snapshot_number: Option, - /// Block set requested - block_set: Option, -} - -impl PeerInfo { - fn can_sync(&self) -> bool { - self.confirmation == ForkConfirmation::Confirmed && !self.expired - } - - fn is_allowed(&self) -> bool { - self.confirmation != ForkConfirmation::Unconfirmed && !self.expired - } - - fn reset_asking(&mut self) { - self.asking_blocks.clear(); - self.asking_hash = None; - // mark any pending requests as expired - if self.asking != PeerAsking::Nothing && self.is_allowed() { - self.expired = true; - } - } -} - -#[cfg(not(test))] -mod random { - use rand; - pub fn new() -> rand::ThreadRng { rand::thread_rng() } -} -#[cfg(test)] -mod random { - use rand::{self, SeedableRng}; - pub fn new() -> rand::XorShiftRng { rand::XorShiftRng::from_seed([0, 1, 2, 3]) } -} - -/// Blockchain sync handler. -/// See module documentation for more details. -pub struct ChainSync { - /// Sync state - state: SyncState, - /// Last block number for the start of sync - starting_block: BlockNumber, - /// Highest block number seen - highest_block: Option, - /// All connected peers - peers: HashMap, - /// Peers active for current sync round - active_peers: HashSet, - /// Block download process for new blocks - new_blocks: BlockDownloader, - /// Block download process for ancient blocks - old_blocks: Option, - /// Last propagated block number - last_sent_block_number: BlockNumber, - /// Network ID - network_id: u64, - /// Optional fork block to check - fork_block: Option<(BlockNumber, H256)>, - /// Snapshot downloader. - snapshot: Snapshot, - /// Connected peers pending Status message. - /// Value is request timestamp. - handshaking_peers: HashMap, - /// Sync start timestamp. Measured when first peer is connected - sync_start_time: Option, - /// Transactions propagation statistics - transactions_stats: TransactionsStats, - /// Enable ancient block downloading - download_old_blocks: bool, - /// Enable warp sync. - enable_warp_sync: bool, -} - -type RlpResponseResult = Result, PacketDecodeError>; - -impl ChainSync { - /// Create a new instance of syncing strategy. - pub fn new(config: SyncConfig, chain: &BlockChainClient) -> ChainSync { - let chain_info = chain.chain_info(); - let mut sync = ChainSync { - state: if config.warp_sync { SyncState::WaitingPeers } else { SyncState::Idle }, - starting_block: chain.chain_info().best_block_number, - highest_block: None, - peers: HashMap::new(), - handshaking_peers: HashMap::new(), - active_peers: HashSet::new(), - new_blocks: BlockDownloader::new(false, &chain_info.best_block_hash, chain_info.best_block_number), - old_blocks: None, - last_sent_block_number: 0, - network_id: config.network_id, - fork_block: config.fork_block, - download_old_blocks: config.download_old_blocks, - snapshot: Snapshot::new(), - sync_start_time: None, - transactions_stats: TransactionsStats::default(), - enable_warp_sync: config.warp_sync, - }; - sync.update_targets(chain); - sync - } - - /// Returns synchonization status - pub fn status(&self) -> SyncStatus { - let last_imported_number = self.new_blocks.last_imported_block_number(); - SyncStatus { - state: self.state.clone(), - protocol_version: PROTOCOL_VERSION_63, - network_id: self.network_id, - start_block_number: self.starting_block, - last_imported_block_number: Some(last_imported_number), - last_imported_old_block_number: self.old_blocks.as_ref().map(|d| d.last_imported_block_number()), - highest_block_number: self.highest_block.map(|n| cmp::max(n, last_imported_number)), - blocks_received: if last_imported_number > self.starting_block { last_imported_number - self.starting_block } else { 0 }, - blocks_total: match self.highest_block { Some(x) if x > self.starting_block => x - self.starting_block, _ => 0 }, - num_peers: self.peers.values().filter(|p| p.is_allowed()).count(), - num_active_peers: self.peers.values().filter(|p| p.is_allowed() && p.asking != PeerAsking::Nothing).count(), - num_snapshot_chunks: self.snapshot.total_chunks(), - snapshot_chunks_done: self.snapshot.done_chunks(), - mem_used: - self.new_blocks.heap_size() - + self.old_blocks.as_ref().map_or(0, |d| d.heap_size()) - + self.peers.heap_size_of_children(), - } - } - - /// Returns information on peers connections - pub fn peer_info(&self, peer_id: &PeerId) -> Option { - self.peers.get(peer_id).map(|peer_data| { - PeerInfoDigest { - version: peer_data.protocol_version as u32, - difficulty: peer_data.difficulty, - head: peer_data.latest_hash, - } - }) - } - - /// Returns transactions propagation statistics - pub fn transactions_stats(&self) -> &H256FastMap { - self.transactions_stats.stats() - } - - /// Updates transactions were received by a peer - pub fn transactions_received(&mut self, hashes: Vec, peer_id: PeerId) { - if let Some(peer_info) = self.peers.get_mut(&peer_id) { - peer_info.last_sent_transactions.extend(&hashes); - } - } - - /// Abort all sync activity - pub fn abort(&mut self, io: &mut SyncIo) { - self.reset_and_continue(io); - self.peers.clear(); - } - - /// Reset sync. Clear all downloaded data but keep the queue - fn reset(&mut self, io: &mut SyncIo) { - self.new_blocks.reset(); - let chain_info = io.chain().chain_info(); - for (_, ref mut p) in &mut self.peers { - if p.block_set != Some(BlockSet::OldBlocks) { - p.reset_asking(); - if p.difficulty.is_none() { - // assume peer has up to date difficulty - p.difficulty = Some(chain_info.pending_total_difficulty); - } - } - } - self.state = SyncState::Idle; - // Reactivate peers only if some progress has been made - // since the last sync round of if starting fresh. - self.active_peers = self.peers.keys().cloned().collect(); - } - - /// Restart sync - pub fn reset_and_continue(&mut self, io: &mut SyncIo) { - trace!(target: "sync", "Restarting"); - if self.state == SyncState::SnapshotData { - debug!(target:"sync", "Aborting snapshot restore"); - io.snapshot_service().abort_restore(); - } - self.snapshot.clear(); - self.reset(io); - self.continue_sync(io); - } - - /// Remove peer from active peer set. Peer will be reactivated on the next sync - /// round. - fn deactivate_peer(&mut self, _io: &mut SyncIo, peer_id: PeerId) { - trace!(target: "sync", "Deactivating peer {}", peer_id); - self.active_peers.remove(&peer_id); - } - - fn maybe_start_snapshot_sync(&mut self, io: &mut SyncIo) { - if !self.enable_warp_sync || io.snapshot_service().supported_versions().is_none() { - return; - } - if self.state != SyncState::WaitingPeers && self.state != SyncState::Blocks && self.state != SyncState::Waiting { - return; - } - // Make sure the snapshot block is not too far away from best block and network best block and - // that it is higher than fork detection block - let our_best_block = io.chain().chain_info().best_block_number; - let fork_block = self.fork_block.as_ref().map(|&(n, _)| n).unwrap_or(0); - - let (best_hash, max_peers, snapshot_peers) = { - //collect snapshot infos from peers - let snapshots = self.peers.iter() - .filter(|&(_, p)| p.is_allowed() && p.snapshot_number.map_or(false, |sn| - our_best_block < sn && (sn - our_best_block) > SNAPSHOT_RESTORE_THRESHOLD && - sn > fork_block && - self.highest_block.map_or(true, |highest| highest >= sn && (highest - sn) <= SNAPSHOT_RESTORE_THRESHOLD) - )) - .filter_map(|(p, peer)| peer.snapshot_hash.map(|hash| (p, hash.clone()))) - .filter(|&(_, ref hash)| !self.snapshot.is_known_bad(hash)); - - let mut snapshot_peers = HashMap::new(); - let mut max_peers: usize = 0; - let mut best_hash = None; - for (p, hash) in snapshots { - let peers = snapshot_peers.entry(hash).or_insert_with(Vec::new); - peers.push(*p); - if peers.len() > max_peers { - max_peers = peers.len(); - best_hash = Some(hash); - } - } - (best_hash, max_peers, snapshot_peers) - }; - - let timeout = (self.state == SyncState::WaitingPeers) && self.sync_start_time.map_or(false, |t| t.elapsed().as_secs() > WAIT_PEERS_TIMEOUT_SEC); - - if let (Some(hash), Some(peers)) = (best_hash, best_hash.map_or(None, |h| snapshot_peers.get(&h))) { - if max_peers >= SNAPSHOT_MIN_PEERS { - trace!(target: "sync", "Starting confirmed snapshot sync {:?} with {:?}", hash, peers); - self.start_snapshot_sync(io, peers); - } else if timeout { - trace!(target: "sync", "Starting unconfirmed snapshot sync {:?} with {:?}", hash, peers); - self.start_snapshot_sync(io, peers); - } - } else if timeout { - trace!(target: "sync", "No snapshots found, starting full sync"); - self.state = SyncState::Idle; - self.continue_sync(io); - } - } - - fn start_snapshot_sync(&mut self, io: &mut SyncIo, peers: &[PeerId]) { - if !self.snapshot.have_manifest() { - for p in peers { - if self.peers.get(p).map_or(false, |p| p.asking == PeerAsking::Nothing) { - self.request_snapshot_manifest(io, *p); - } - } - self.state = SyncState::SnapshotManifest; - trace!(target: "sync", "New snapshot sync with {:?}", peers); - } else { - self.state = SyncState::SnapshotData; - trace!(target: "sync", "Resumed snapshot sync with {:?}", peers); - } - } - - /// Restart sync disregarding the block queue status. May end up re-downloading up to QUEUE_SIZE blocks - pub fn restart(&mut self, io: &mut SyncIo) { - self.update_targets(io.chain()); - self.reset_and_continue(io); - } - - /// Update sync after the blockchain has been changed externally. - pub fn update_targets(&mut self, chain: &BlockChainClient) { - // Do not assume that the block queue/chain still has our last_imported_block - let chain = chain.chain_info(); - self.new_blocks = BlockDownloader::new(false, &chain.best_block_hash, chain.best_block_number); - self.old_blocks = None; - if self.download_old_blocks { - if let (Some(ancient_block_hash), Some(ancient_block_number)) = (chain.ancient_block_hash, chain.ancient_block_number) { - - trace!(target: "sync", "Downloading old blocks from {:?} (#{}) till {:?} (#{:?})", ancient_block_hash, ancient_block_number, chain.first_block_hash, chain.first_block_number); - let mut downloader = BlockDownloader::with_unlimited_reorg(true, &ancient_block_hash, ancient_block_number); - if let Some(hash) = chain.first_block_hash { - trace!(target: "sync", "Downloader target set to {:?}", hash); - downloader.set_target(&hash); - } - self.old_blocks = Some(downloader); - } - } - } - - /// Called by peer to report status - fn on_peer_status(&mut self, io: &mut SyncIo, peer_id: PeerId, r: &UntrustedRlp) -> Result<(), PacketDecodeError> { - self.handshaking_peers.remove(&peer_id); - let protocol_version: u8 = r.val_at(0)?; - let warp_protocol = io.protocol_version(&WARP_SYNC_PROTOCOL_ID, peer_id) != 0; - let peer = PeerInfo { - protocol_version: protocol_version, - network_id: r.val_at(1)?, - difficulty: Some(r.val_at(2)?), - latest_hash: r.val_at(3)?, - genesis: r.val_at(4)?, - asking: PeerAsking::Nothing, - asking_blocks: Vec::new(), - asking_hash: None, - ask_time: Instant::now(), - last_sent_transactions: HashSet::new(), - expired: false, - confirmation: if self.fork_block.is_none() { ForkConfirmation::Confirmed } else { ForkConfirmation::Unconfirmed }, - asking_snapshot_data: None, - snapshot_hash: if warp_protocol { Some(r.val_at(5)?) } else { None }, - snapshot_number: if warp_protocol { Some(r.val_at(6)?) } else { None }, - block_set: None, - }; - - if self.sync_start_time.is_none() { - self.sync_start_time = Some(Instant::now()); - } - - trace!(target: "sync", "New peer {} (protocol: {}, network: {:?}, difficulty: {:?}, latest:{}, genesis:{}, snapshot:{:?})", - peer_id, peer.protocol_version, peer.network_id, peer.difficulty, peer.latest_hash, peer.genesis, peer.snapshot_number); - if io.is_expired() { - trace!(target: "sync", "Status packet from expired session {}:{}", peer_id, io.peer_info(peer_id)); - return Ok(()); - } - - if self.peers.contains_key(&peer_id) { - debug!(target: "sync", "Unexpected status packet from {}:{}", peer_id, io.peer_info(peer_id)); - return Ok(()); - } - let chain_info = io.chain().chain_info(); - if peer.genesis != chain_info.genesis_hash { - io.disable_peer(peer_id); - trace!(target: "sync", "Peer {} genesis hash mismatch (ours: {}, theirs: {})", peer_id, chain_info.genesis_hash, peer.genesis); - return Ok(()); - } - if peer.network_id != self.network_id { - io.disable_peer(peer_id); - trace!(target: "sync", "Peer {} network id mismatch (ours: {}, theirs: {})", peer_id, self.network_id, peer.network_id); - return Ok(()); - } - if (warp_protocol && peer.protocol_version != PROTOCOL_VERSION_1 && peer.protocol_version != PROTOCOL_VERSION_2) || (!warp_protocol && peer.protocol_version != PROTOCOL_VERSION_63 && peer.protocol_version != PROTOCOL_VERSION_62) { - io.disable_peer(peer_id); - trace!(target: "sync", "Peer {} unsupported eth protocol ({})", peer_id, peer.protocol_version); - return Ok(()); - } - - self.peers.insert(peer_id.clone(), peer); - // Don't activate peer immediatelly when searching for common block. - // Let the current sync round complete first. - self.active_peers.insert(peer_id.clone()); - debug!(target: "sync", "Connected {}:{}", peer_id, io.peer_info(peer_id)); - if let Some((fork_block, _)) = self.fork_block { - self.request_fork_header_by_number(io, peer_id, fork_block); - } else { - self.sync_peer(io, peer_id, false); - } - Ok(()) - } - - /// Called by peer once it has new block headers during sync - fn on_peer_block_headers(&mut self, io: &mut SyncIo, peer_id: PeerId, r: &UntrustedRlp) -> Result<(), PacketDecodeError> { - let confirmed = match self.peers.get_mut(&peer_id) { - Some(ref mut peer) if peer.asking == PeerAsking::ForkHeader => { - peer.asking = PeerAsking::Nothing; - let item_count = r.item_count()?; - let (fork_number, fork_hash) = self.fork_block.expect("ForkHeader request is sent only fork block is Some; qed").clone(); - if item_count == 0 || item_count != 1 { - trace!(target: "sync", "{}: Chain is too short to confirm the block", peer_id); - peer.confirmation = ForkConfirmation::TooShort; - } else { - let header = r.at(0)?.as_raw(); - if keccak(&header) == fork_hash { - trace!(target: "sync", "{}: Confirmed peer", peer_id); - peer.confirmation = ForkConfirmation::Confirmed; - if !io.chain_overlay().read().contains_key(&fork_number) { - io.chain_overlay().write().insert(fork_number, header.to_vec()); - } - } else { - trace!(target: "sync", "{}: Fork mismatch", peer_id); - io.disable_peer(peer_id); - return Ok(()); - } - } - true - }, - _ => false, - }; - if confirmed { - self.sync_peer(io, peer_id, false); - return Ok(()); - } - - self.clear_peer_download(peer_id); - let expected_hash = self.peers.get(&peer_id).and_then(|p| p.asking_hash); - let allowed = self.peers.get(&peer_id).map(|p| p.is_allowed()).unwrap_or(false); - let block_set = self.peers.get(&peer_id).and_then(|p| p.block_set).unwrap_or(BlockSet::NewBlocks); - if !self.reset_peer_asking(peer_id, PeerAsking::BlockHeaders) || expected_hash.is_none() || !allowed { - trace!(target: "sync", "{}: Ignored unexpected headers, expected_hash = {:?}", peer_id, expected_hash); - self.continue_sync(io); - return Ok(()); - } - let item_count = r.item_count()?; - trace!(target: "sync", "{} -> BlockHeaders ({} entries), state = {:?}, set = {:?}", peer_id, item_count, self.state, block_set); - if (self.state == SyncState::Idle || self.state == SyncState::WaitingPeers) && self.old_blocks.is_none() { - trace!(target: "sync", "Ignored unexpected block headers"); - self.continue_sync(io); - return Ok(()); - } - if self.state == SyncState::Waiting { - trace!(target: "sync", "Ignored block headers while waiting"); - self.continue_sync(io); - return Ok(()); - } - - let result = { - let downloader = match block_set { - BlockSet::NewBlocks => &mut self.new_blocks, - BlockSet::OldBlocks => { - match self.old_blocks { - None => { - trace!(target: "sync", "Ignored block headers while block download is inactive"); - self.continue_sync(io); - return Ok(()); - }, - Some(ref mut blocks) => blocks, - } - } - }; - downloader.import_headers(io, r, expected_hash) - }; - - match result { - Err(DownloaderImportError::Useless) => { - self.deactivate_peer(io, peer_id); - }, - Err(DownloaderImportError::Invalid) => { - io.disable_peer(peer_id); - self.deactivate_peer(io, peer_id); - self.continue_sync(io); - return Ok(()); - }, - Ok(DownloadAction::Reset) => { - // mark all outstanding requests as expired - trace!("Resetting downloads for {:?}", block_set); - for (_, ref mut p) in self.peers.iter_mut().filter(|&(_, ref p)| p.block_set == Some(block_set)) { - p.reset_asking(); - } - - } - Ok(DownloadAction::None) => {}, - } - - self.collect_blocks(io, block_set); - // give a task to the same peer first if received valuable headers. - self.sync_peer(io, peer_id, false); - // give tasks to other peers - self.continue_sync(io); - Ok(()) - } - - /// Called by peer once it has new block bodies - fn on_peer_block_bodies(&mut self, io: &mut SyncIo, peer_id: PeerId, r: &UntrustedRlp) -> Result<(), PacketDecodeError> { - self.clear_peer_download(peer_id); - let block_set = self.peers.get(&peer_id).and_then(|p| p.block_set).unwrap_or(BlockSet::NewBlocks); - if !self.reset_peer_asking(peer_id, PeerAsking::BlockBodies) { - trace!(target: "sync", "{}: Ignored unexpected bodies", peer_id); - self.continue_sync(io); - return Ok(()); - } - let item_count = r.item_count()?; - trace!(target: "sync", "{} -> BlockBodies ({} entries), set = {:?}", peer_id, item_count, block_set); - if item_count == 0 { - self.deactivate_peer(io, peer_id); - } - else if self.state == SyncState::Waiting { - trace!(target: "sync", "Ignored block bodies while waiting"); - } - else - { - let result = { - let downloader = match block_set { - BlockSet::NewBlocks => &mut self.new_blocks, - BlockSet::OldBlocks => match self.old_blocks { - None => { - trace!(target: "sync", "Ignored block headers while block download is inactive"); - self.continue_sync(io); - return Ok(()); - }, - Some(ref mut blocks) => blocks, - } - }; - downloader.import_bodies(io, r) - }; - - match result { - Err(DownloaderImportError::Invalid) => { - io.disable_peer(peer_id); - self.deactivate_peer(io, peer_id); - self.continue_sync(io); - return Ok(()); - }, - Err(DownloaderImportError::Useless) => { - self.deactivate_peer(io, peer_id); - }, - Ok(()) => (), - } - - self.collect_blocks(io, block_set); - self.sync_peer(io, peer_id, false); - } - self.continue_sync(io); - Ok(()) - } - - /// Called by peer once it has new block receipts - fn on_peer_block_receipts(&mut self, io: &mut SyncIo, peer_id: PeerId, r: &UntrustedRlp) -> Result<(), PacketDecodeError> { - self.clear_peer_download(peer_id); - let block_set = self.peers.get(&peer_id).and_then(|p| p.block_set).unwrap_or(BlockSet::NewBlocks); - if !self.reset_peer_asking(peer_id, PeerAsking::BlockReceipts) { - trace!(target: "sync", "{}: Ignored unexpected receipts", peer_id); - self.continue_sync(io); - return Ok(()); - } - let item_count = r.item_count()?; - trace!(target: "sync", "{} -> BlockReceipts ({} entries)", peer_id, item_count); - if item_count == 0 { - self.deactivate_peer(io, peer_id); - } - else if self.state == SyncState::Waiting { - trace!(target: "sync", "Ignored block receipts while waiting"); - } - else - { - let result = { - let downloader = match block_set { - BlockSet::NewBlocks => &mut self.new_blocks, - BlockSet::OldBlocks => match self.old_blocks { - None => { - trace!(target: "sync", "Ignored block headers while block download is inactive"); - self.continue_sync(io); - return Ok(()); - }, - Some(ref mut blocks) => blocks, - } - }; - downloader.import_receipts(io, r) - }; - - match result { - Err(DownloaderImportError::Invalid) => { - io.disable_peer(peer_id); - self.deactivate_peer(io, peer_id); - self.continue_sync(io); - return Ok(()); - }, - Err(DownloaderImportError::Useless) => { - self.deactivate_peer(io, peer_id); - }, - Ok(()) => (), - } - - self.collect_blocks(io, block_set); - self.sync_peer(io, peer_id, false); - } - self.continue_sync(io); - Ok(()) - } - - /// Called by peer once it has new block bodies - fn on_peer_new_block(&mut self, io: &mut SyncIo, peer_id: PeerId, r: &UntrustedRlp) -> Result<(), PacketDecodeError> { - if !self.peers.get(&peer_id).map_or(false, |p| p.can_sync()) { - trace!(target: "sync", "Ignoring new block from unconfirmed peer {}", peer_id); - return Ok(()); - } - let difficulty: U256 = r.val_at(1)?; - if let Some(ref mut peer) = self.peers.get_mut(&peer_id) { - if peer.difficulty.map_or(true, |pd| difficulty > pd) { - peer.difficulty = Some(difficulty); - } - } - let block_rlp = r.at(0)?; - let header_rlp = block_rlp.at(0)?; - let h = keccak(&header_rlp.as_raw()); - trace!(target: "sync", "{} -> NewBlock ({})", peer_id, h); - let header: BlockHeader = header_rlp.as_val()?; - if header.number() > self.highest_block.unwrap_or(0) { - self.highest_block = Some(header.number()); - } - let mut unknown = false; - { - if let Some(ref mut peer) = self.peers.get_mut(&peer_id) { - peer.latest_hash = header.hash(); - } - } - let last_imported_number = self.new_blocks.last_imported_block_number(); - if last_imported_number > header.number() && last_imported_number - header.number() > MAX_NEW_BLOCK_AGE { - trace!(target: "sync", "Ignored ancient new block {:?}", h); - io.disable_peer(peer_id); - return Ok(()); - } - match io.chain().import_block(block_rlp.as_raw().to_vec()) { - Err(BlockImportError::Import(ImportError::AlreadyInChain)) => { - trace!(target: "sync", "New block already in chain {:?}", h); - }, - Err(BlockImportError::Import(ImportError::AlreadyQueued)) => { - trace!(target: "sync", "New block already queued {:?}", h); - }, - Ok(_) => { - // abort current download of the same block - self.complete_sync(io); - self.new_blocks.mark_as_known(&header.hash(), header.number()); - trace!(target: "sync", "New block queued {:?} ({})", h, header.number()); - }, - Err(BlockImportError::Block(BlockError::UnknownParent(p))) => { - unknown = true; - trace!(target: "sync", "New block with unknown parent ({:?}) {:?}", p, h); - }, - Err(e) => { - debug!(target: "sync", "Bad new block {:?} : {:?}", h, e); - io.disable_peer(peer_id); - } - }; - if unknown { - if self.state != SyncState::Idle { - trace!(target: "sync", "NewBlock ignored while seeking"); - } else { - trace!(target: "sync", "New unknown block {:?}", h); - //TODO: handle too many unknown blocks - self.sync_peer(io, peer_id, true); - } - } - self.continue_sync(io); - Ok(()) - } - - /// Handles `NewHashes` packet. Initiates headers download for any unknown hashes. - fn on_peer_new_hashes(&mut self, io: &mut SyncIo, peer_id: PeerId, r: &UntrustedRlp) -> Result<(), PacketDecodeError> { - if !self.peers.get(&peer_id).map_or(false, |p| p.can_sync()) { - trace!(target: "sync", "Ignoring new hashes from unconfirmed peer {}", peer_id); - return Ok(()); - } - let hashes: Vec<_> = r.iter().take(MAX_NEW_HASHES).map(|item| (item.val_at::(0), item.val_at::(1))).collect(); - if let Some(ref mut peer) = self.peers.get_mut(&peer_id) { - // Peer has new blocks with unknown difficulty - peer.difficulty = None; - if let Some(&(Ok(ref h), _)) = hashes.last() { - peer.latest_hash = h.clone(); - } - } - if self.state != SyncState::Idle { - trace!(target: "sync", "Ignoring new hashes since we're already downloading."); - let max = r.iter().take(MAX_NEW_HASHES).map(|item| item.val_at::(1).unwrap_or(0)).fold(0u64, cmp::max); - if max > self.highest_block.unwrap_or(0) { - self.highest_block = Some(max); - } - self.continue_sync(io); - return Ok(()); - } - trace!(target: "sync", "{} -> NewHashes ({} entries)", peer_id, r.item_count()?); - let mut max_height: BlockNumber = 0; - let mut new_hashes = Vec::new(); - let last_imported_number = self.new_blocks.last_imported_block_number(); - for (rh, rn) in hashes { - let hash = rh?; - let number = rn?; - if number > self.highest_block.unwrap_or(0) { - self.highest_block = Some(number); - } - if self.new_blocks.is_downloading(&hash) { - continue; - } - if last_imported_number > number && last_imported_number - number > MAX_NEW_BLOCK_AGE { - trace!(target: "sync", "Ignored ancient new block hash {:?}", hash); - io.disable_peer(peer_id); - continue; - } - match io.chain().block_status(BlockId::Hash(hash.clone())) { - BlockStatus::InChain => { - trace!(target: "sync", "New block hash already in chain {:?}", hash); - }, - BlockStatus::Queued => { - trace!(target: "sync", "New hash block already queued {:?}", hash); - }, - BlockStatus::Unknown | BlockStatus::Pending => { - new_hashes.push(hash.clone()); - if number > max_height { - trace!(target: "sync", "New unknown block hash {:?}", hash); - if let Some(ref mut peer) = self.peers.get_mut(&peer_id) { - peer.latest_hash = hash.clone(); - } - max_height = number; - } - }, - BlockStatus::Bad => { - debug!(target: "sync", "Bad new block hash {:?}", hash); - io.disable_peer(peer_id); - return Ok(()); - } - } - }; - if max_height != 0 { - trace!(target: "sync", "Downloading blocks for new hashes"); - self.new_blocks.reset_to(new_hashes); - self.state = SyncState::NewBlocks; - self.sync_peer(io, peer_id, true); - } - self.continue_sync(io); - Ok(()) - } - - /// Called when snapshot manifest is downloaded from a peer. - fn on_snapshot_manifest(&mut self, io: &mut SyncIo, peer_id: PeerId, r: &UntrustedRlp) -> Result<(), PacketDecodeError> { - if !self.peers.get(&peer_id).map_or(false, |p| p.can_sync()) { - trace!(target: "sync", "Ignoring snapshot manifest from unconfirmed peer {}", peer_id); - return Ok(()); - } - self.clear_peer_download(peer_id); - if !self.reset_peer_asking(peer_id, PeerAsking::SnapshotManifest) || self.state != SyncState::SnapshotManifest { - trace!(target: "sync", "{}: Ignored unexpected/expired manifest", peer_id); - self.continue_sync(io); - return Ok(()); - } - - let manifest_rlp = r.at(0)?; - let manifest = match ManifestData::from_rlp(manifest_rlp.as_raw()) { - Err(e) => { - trace!(target: "sync", "{}: Ignored bad manifest: {:?}", peer_id, e); - io.disable_peer(peer_id); - self.continue_sync(io); - return Ok(()); - } - Ok(manifest) => manifest, - }; - - let is_supported_version = io.snapshot_service().supported_versions() - .map_or(false, |(l, h)| manifest.version >= l && manifest.version <= h); - - if !is_supported_version { - trace!(target: "sync", "{}: Snapshot manifest version not supported: {}", peer_id, manifest.version); - io.disable_peer(peer_id); - self.continue_sync(io); - return Ok(()); - } - self.snapshot.reset_to(&manifest, &keccak(manifest_rlp.as_raw())); - io.snapshot_service().begin_restore(manifest); - self.state = SyncState::SnapshotData; - - // give a task to the same peer first. - self.sync_peer(io, peer_id, false); - // give tasks to other peers - self.continue_sync(io); - Ok(()) - } - - /// Called when snapshot data is downloaded from a peer. - fn on_snapshot_data(&mut self, io: &mut SyncIo, peer_id: PeerId, r: &UntrustedRlp) -> Result<(), PacketDecodeError> { - if !self.peers.get(&peer_id).map_or(false, |p| p.can_sync()) { - trace!(target: "sync", "Ignoring snapshot data from unconfirmed peer {}", peer_id); - return Ok(()); - } - self.clear_peer_download(peer_id); - if !self.reset_peer_asking(peer_id, PeerAsking::SnapshotData) || (self.state != SyncState::SnapshotData && self.state != SyncState::SnapshotWaiting) { - trace!(target: "sync", "{}: Ignored unexpected snapshot data", peer_id); - self.continue_sync(io); - return Ok(()); - } - - // check service status - let status = io.snapshot_service().status(); - match status { - RestorationStatus::Inactive | RestorationStatus::Failed => { - trace!(target: "sync", "{}: Snapshot restoration aborted", peer_id); - self.state = SyncState::WaitingPeers; - - // only note bad if restoration failed. - if let (Some(hash), RestorationStatus::Failed) = (self.snapshot.snapshot_hash(), status) { - trace!(target: "sync", "Noting snapshot hash {} as bad", hash); - self.snapshot.note_bad(hash); - } - - self.snapshot.clear(); - self.continue_sync(io); - return Ok(()); - }, - RestorationStatus::Ongoing { .. } => { - trace!(target: "sync", "{}: Snapshot restoration is ongoing", peer_id); - }, - } - - let snapshot_data: Bytes = r.val_at(0)?; - match self.snapshot.validate_chunk(&snapshot_data) { - Ok(ChunkType::Block(hash)) => { - trace!(target: "sync", "{}: Processing block chunk", peer_id); - io.snapshot_service().restore_block_chunk(hash, snapshot_data); - } - Ok(ChunkType::State(hash)) => { - trace!(target: "sync", "{}: Processing state chunk", peer_id); - io.snapshot_service().restore_state_chunk(hash, snapshot_data); - } - Err(()) => { - trace!(target: "sync", "{}: Got bad snapshot chunk", peer_id); - io.disconnect_peer(peer_id); - self.continue_sync(io); - return Ok(()); - } - } - - if self.snapshot.is_complete() { - // wait for snapshot restoration process to complete - self.state = SyncState::SnapshotWaiting; - } - // give a task to the same peer first. - self.sync_peer(io, peer_id, false); - // give tasks to other peers - self.continue_sync(io); - Ok(()) - } - - /// Called by peer when it is disconnecting - pub fn on_peer_aborting(&mut self, io: &mut SyncIo, peer: PeerId) { - trace!(target: "sync", "== Disconnecting {}: {}", peer, io.peer_info(peer)); - self.handshaking_peers.remove(&peer); - if self.peers.contains_key(&peer) { - debug!(target: "sync", "Disconnected {}", peer); - self.clear_peer_download(peer); - self.peers.remove(&peer); - self.active_peers.remove(&peer); - self.continue_sync(io); - } - } - - /// Called when a new peer is connected - pub fn on_peer_connected(&mut self, io: &mut SyncIo, peer: PeerId) { - trace!(target: "sync", "== Connected {}: {}", peer, io.peer_info(peer)); - if let Err(e) = self.send_status(io, peer) { - debug!(target:"sync", "Error sending status request: {:?}", e); - io.disconnect_peer(peer); - } else { - self.handshaking_peers.insert(peer, Instant::now()); - } - } - - /// Resume downloading - fn continue_sync(&mut self, io: &mut SyncIo) { - let mut peers: Vec<(PeerId, U256, u8)> = self.peers.iter().filter_map(|(k, p)| - if p.can_sync() { Some((*k, p.difficulty.unwrap_or_else(U256::zero), p.protocol_version)) } else { None }).collect(); - random::new().shuffle(&mut peers); //TODO: sort by rating - // prefer peers with higher protocol version - peers.sort_by(|&(_, _, ref v1), &(_, _, ref v2)| v1.cmp(v2)); - trace!(target: "sync", "Syncing with peers: {} active, {} confirmed, {} total", self.active_peers.len(), peers.len(), self.peers.len()); - for (p, _, _) in peers { - if self.active_peers.contains(&p) { - self.sync_peer(io, p, false); - } - } - if (self.state != SyncState::WaitingPeers && self.state != SyncState::SnapshotWaiting && self.state != SyncState::Waiting && self.state != SyncState::Idle) - && !self.peers.values().any(|p| p.asking != PeerAsking::Nothing && p.block_set != Some(BlockSet::OldBlocks) && p.can_sync()) { - - self.complete_sync(io); - } - } - - /// Called after all blocks have been downloaded - fn complete_sync(&mut self, io: &mut SyncIo) { - trace!(target: "sync", "Sync complete"); - self.reset(io); - self.state = SyncState::Idle; - } - - /// Enter waiting state - fn pause_sync(&mut self) { - trace!(target: "sync", "Block queue full, pausing sync"); - self.state = SyncState::Waiting; - } - - /// Find something to do for a peer. Called for a new peer or when a peer is done with its task. - fn sync_peer(&mut self, io: &mut SyncIo, peer_id: PeerId, force: bool) { - if !self.active_peers.contains(&peer_id) { - trace!(target: "sync", "Skipping deactivated peer {}", peer_id); - return; - } - let (peer_latest, peer_difficulty, peer_snapshot_number, peer_snapshot_hash) = { - if let Some(peer) = self.peers.get_mut(&peer_id) { - if peer.asking != PeerAsking::Nothing || !peer.can_sync() { - trace!(target: "sync", "Skipping busy peer {}", peer_id); - return; - } - if self.state == SyncState::Waiting { - trace!(target: "sync", "Waiting for the block queue"); - return; - } - if self.state == SyncState::SnapshotWaiting { - trace!(target: "sync", "Waiting for the snapshot restoration"); - return; - } - (peer.latest_hash.clone(), peer.difficulty.clone(), peer.snapshot_number.as_ref().cloned().unwrap_or(0), peer.snapshot_hash.as_ref().cloned()) - } else { - return; - } - }; - let chain_info = io.chain().chain_info(); - let syncing_difficulty = chain_info.pending_total_difficulty; - let num_active_peers = self.peers.values().filter(|p| p.asking != PeerAsking::Nothing).count(); - - let higher_difficulty = peer_difficulty.map_or(true, |pd| pd > syncing_difficulty); - if force || higher_difficulty || self.old_blocks.is_some() { - match self.state { - SyncState::WaitingPeers => { - trace!(target: "sync", "Checking snapshot sync: {} vs {}", peer_snapshot_number, chain_info.best_block_number); - self.maybe_start_snapshot_sync(io); - }, - SyncState::Idle | SyncState::Blocks | SyncState::NewBlocks => { - if io.chain().queue_info().is_full() { - self.pause_sync(); - return; - } - - let have_latest = io.chain().block_status(BlockId::Hash(peer_latest)) != BlockStatus::Unknown; - trace!(target: "sync", "Considering peer {}, force={}, td={:?}, our td={}, latest={}, have_latest={}, state={:?}", peer_id, force, peer_difficulty, syncing_difficulty, peer_latest, have_latest, self.state); - if !have_latest && (higher_difficulty || force || self.state == SyncState::NewBlocks) { - // check if got new blocks to download - trace!(target: "sync", "Syncing with peer {}, force={}, td={:?}, our td={}, state={:?}", peer_id, force, peer_difficulty, syncing_difficulty, self.state); - if let Some(request) = self.new_blocks.request_blocks(io, num_active_peers) { - self.request_blocks(io, peer_id, request, BlockSet::NewBlocks); - if self.state == SyncState::Idle { - self.state = SyncState::Blocks; - } - return; - } - } - - if let Some(request) = self.old_blocks.as_mut().and_then(|d| d.request_blocks(io, num_active_peers)) { - self.request_blocks(io, peer_id, request, BlockSet::OldBlocks); - return; - } - }, - SyncState::SnapshotData => { - if let RestorationStatus::Ongoing { state_chunks_done, block_chunks_done, .. } = io.snapshot_service().status() { - if self.snapshot.done_chunks() - (state_chunks_done + block_chunks_done) as usize > MAX_SNAPSHOT_CHUNKS_DOWNLOAD_AHEAD { - trace!(target: "sync", "Snapshot queue full, pausing sync"); - self.state = SyncState::SnapshotWaiting; - return; - } - } - if peer_snapshot_hash.is_some() && peer_snapshot_hash == self.snapshot.snapshot_hash() { - self.request_snapshot_data(io, peer_id); - } - }, - SyncState::SnapshotManifest | //already downloading from other peer - SyncState::Waiting | SyncState::SnapshotWaiting => () - } - } else { - trace!(target: "sync", "Skipping peer {}, force={}, td={:?}, our td={}, state={:?}", peer_id, force, peer_difficulty, syncing_difficulty, self.state); - } - } - - /// Perofrm block download request` - fn request_blocks(&mut self, io: &mut SyncIo, peer_id: PeerId, request: BlockRequest, block_set: BlockSet) { - match request { - BlockRequest::Headers { start, count, skip } => { - self.request_headers_by_hash(io, peer_id, &start, count, skip, false, block_set); - }, - BlockRequest::Bodies { hashes } => { - self.request_bodies(io, peer_id, hashes, block_set); - }, - BlockRequest::Receipts { hashes } => { - self.request_receipts(io, peer_id, hashes, block_set); - }, - } - } - - /// Find some headers or blocks to download for a peer. - fn request_snapshot_data(&mut self, io: &mut SyncIo, peer_id: PeerId) { - self.clear_peer_download(peer_id); - // find chunk data to download - if let Some(hash) = self.snapshot.needed_chunk() { - if let Some(ref mut peer) = self.peers.get_mut(&peer_id) { - peer.asking_snapshot_data = Some(hash.clone()); - } - self.request_snapshot_chunk(io, peer_id, &hash); - } - } - - /// Clear all blocks/headers marked as being downloaded by a peer. - fn clear_peer_download(&mut self, peer_id: PeerId) { - if let Some(ref mut peer) = self.peers.get_mut(&peer_id) { - match peer.asking { - PeerAsking::BlockHeaders => { - if let Some(ref hash) = peer.asking_hash { - self.new_blocks.clear_header_download(hash); - if let Some(ref mut old) = self.old_blocks { - old.clear_header_download(hash); - } - } - }, - PeerAsking::BlockBodies => { - self.new_blocks.clear_body_download(&peer.asking_blocks); - if let Some(ref mut old) = self.old_blocks { - old.clear_body_download(&peer.asking_blocks); - } - }, - PeerAsking::BlockReceipts => { - self.new_blocks.clear_receipt_download(&peer.asking_blocks); - if let Some(ref mut old) = self.old_blocks { - old.clear_receipt_download(&peer.asking_blocks); - } - }, - PeerAsking::SnapshotData => { - if let Some(hash) = peer.asking_snapshot_data { - self.snapshot.clear_chunk_download(&hash); - } - }, - _ => (), - } - } - } - - /// Checks if there are blocks fully downloaded that can be imported into the blockchain and does the import. - fn collect_blocks(&mut self, io: &mut SyncIo, block_set: BlockSet) { - match block_set { - BlockSet::NewBlocks => { - if self.new_blocks.collect_blocks(io, self.state == SyncState::NewBlocks) == Err(DownloaderImportError::Invalid) { - self.restart(io); - } - }, - BlockSet::OldBlocks => { - if self.old_blocks.as_mut().map_or(false, |downloader| { downloader.collect_blocks(io, false) == Err(DownloaderImportError::Invalid) }) { - self.restart(io); - } else if self.old_blocks.as_ref().map_or(false, |downloader| { downloader.is_complete() }) { - trace!(target: "sync", "Background block download is complete"); - self.old_blocks = None; - } - } - } - } - - /// Request headers from a peer by block hash - fn request_headers_by_hash(&mut self, sync: &mut SyncIo, peer_id: PeerId, h: &H256, count: u64, skip: u64, reverse: bool, set: BlockSet) { - trace!(target: "sync", "{} <- GetBlockHeaders: {} entries starting from {}, set = {:?}", peer_id, count, h, set); - let mut rlp = RlpStream::new_list(4); - rlp.append(h); - rlp.append(&count); - rlp.append(&skip); - rlp.append(&if reverse {1u32} else {0u32}); - self.send_request(sync, peer_id, PeerAsking::BlockHeaders, GET_BLOCK_HEADERS_PACKET, rlp.out()); - let peer = self.peers.get_mut(&peer_id).expect("peer_id may originate either from on_packet, where it is already validated or from enumerating self.peers. qed"); - peer.asking_hash = Some(h.clone()); - peer.block_set = Some(set); - } - - /// Request headers from a peer by block number - fn request_fork_header_by_number(&mut self, sync: &mut SyncIo, peer_id: PeerId, n: BlockNumber) { - trace!(target: "sync", "{} <- GetForkHeader: at {}", peer_id, n); - let mut rlp = RlpStream::new_list(4); - rlp.append(&n); - rlp.append(&1u32); - rlp.append(&0u32); - rlp.append(&0u32); - self.send_request(sync, peer_id, PeerAsking::ForkHeader, GET_BLOCK_HEADERS_PACKET, rlp.out()); - } - - /// Request snapshot manifest from a peer. - fn request_snapshot_manifest(&mut self, sync: &mut SyncIo, peer_id: PeerId) { - trace!(target: "sync", "{} <- GetSnapshotManifest", peer_id); - let rlp = RlpStream::new_list(0); - self.send_request(sync, peer_id, PeerAsking::SnapshotManifest, GET_SNAPSHOT_MANIFEST_PACKET, rlp.out()); - } - - /// Request snapshot chunk from a peer. - fn request_snapshot_chunk(&mut self, sync: &mut SyncIo, peer_id: PeerId, chunk: &H256) { - trace!(target: "sync", "{} <- GetSnapshotData {:?}", peer_id, chunk); - let mut rlp = RlpStream::new_list(1); - rlp.append(chunk); - self.send_request(sync, peer_id, PeerAsking::SnapshotData, GET_SNAPSHOT_DATA_PACKET, rlp.out()); - } - - /// Request block bodies from a peer - fn request_bodies(&mut self, sync: &mut SyncIo, peer_id: PeerId, hashes: Vec, set: BlockSet) { - let mut rlp = RlpStream::new_list(hashes.len()); - trace!(target: "sync", "{} <- GetBlockBodies: {} entries starting from {:?}, set = {:?}", peer_id, hashes.len(), hashes.first(), set); - for h in &hashes { - rlp.append(&h.clone()); - } - self.send_request(sync, peer_id, PeerAsking::BlockBodies, GET_BLOCK_BODIES_PACKET, rlp.out()); - let peer = self.peers.get_mut(&peer_id).expect("peer_id may originate either from on_packet, where it is already validated or from enumerating self.peers. qed"); - peer.asking_blocks = hashes; - peer.block_set = Some(set); - } - - /// Request block receipts from a peer - fn request_receipts(&mut self, sync: &mut SyncIo, peer_id: PeerId, hashes: Vec, set: BlockSet) { - let mut rlp = RlpStream::new_list(hashes.len()); - trace!(target: "sync", "{} <- GetBlockReceipts: {} entries starting from {:?}, set = {:?}", peer_id, hashes.len(), hashes.first(), set); - for h in &hashes { - rlp.append(&h.clone()); - } - self.send_request(sync, peer_id, PeerAsking::BlockReceipts, GET_RECEIPTS_PACKET, rlp.out()); - let peer = self.peers.get_mut(&peer_id).expect("peer_id may originate either from on_packet, where it is already validated or from enumerating self.peers. qed"); - peer.asking_blocks = hashes; - peer.block_set = Some(set); - } - - /// Reset peer status after request is complete. - fn reset_peer_asking(&mut self, peer_id: PeerId, asking: PeerAsking) -> bool { - if let Some(ref mut peer) = self.peers.get_mut(&peer_id) { - peer.expired = false; - peer.block_set = None; - if peer.asking != asking { - trace!(target:"sync", "Asking {:?} while expected {:?}", peer.asking, asking); - peer.asking = PeerAsking::Nothing; - return false; - } else { - peer.asking = PeerAsking::Nothing; - return true; - } - } - false - } - - /// Generic request sender - fn send_request(&mut self, sync: &mut SyncIo, peer_id: PeerId, asking: PeerAsking, packet_id: PacketId, packet: Bytes) { - if let Some(ref mut peer) = self.peers.get_mut(&peer_id) { - if peer.asking != PeerAsking::Nothing { - warn!(target:"sync", "Asking {:?} while requesting {:?}", peer.asking, asking); - } - peer.asking = asking; - peer.ask_time = Instant::now(); - let result = if packet_id >= ETH_PACKET_COUNT { - sync.send_protocol(WARP_SYNC_PROTOCOL_ID, peer_id, packet_id, packet) - } else { - sync.send(peer_id, packet_id, packet) - }; - if let Err(e) = result { - debug!(target:"sync", "Error sending request: {:?}", e); - sync.disconnect_peer(peer_id); - } - } - } - - /// Generic packet sender - fn send_packet(&mut self, sync: &mut SyncIo, peer_id: PeerId, packet_id: PacketId, packet: Bytes) { - if let Err(e) = sync.send(peer_id, packet_id, packet) { - debug!(target:"sync", "Error sending packet: {:?}", e); - sync.disconnect_peer(peer_id); - } - } - - /// Called when peer sends us new transactions - fn on_peer_transactions(&mut self, io: &mut SyncIo, peer_id: PeerId, r: &UntrustedRlp) -> Result<(), PacketDecodeError> { - // Accept transactions only when fully synced - if !io.is_chain_queue_empty() || (self.state != SyncState::Idle && self.state != SyncState::NewBlocks) { - trace!(target: "sync", "{} Ignoring transactions while syncing", peer_id); - return Ok(()); - } - if !self.peers.get(&peer_id).map_or(false, |p| p.can_sync()) { - trace!(target: "sync", "{} Ignoring transactions from unconfirmed/unknown peer", peer_id); - } - - let item_count = r.item_count()?; - trace!(target: "sync", "{:02} -> Transactions ({} entries)", peer_id, item_count); - let mut transactions = Vec::with_capacity(item_count); - for i in 0 .. item_count { - let rlp = r.at(i)?; - if rlp.as_raw().len() > MAX_TRANSACTION_SIZE { - debug!("Skipped oversized transaction of {} bytes", rlp.as_raw().len()); - continue; - } - let tx = rlp.as_raw().to_vec(); - transactions.push(tx); - } - io.chain().queue_transactions(transactions, peer_id); - Ok(()) - } - - /// Send Status message - fn send_status(&mut self, io: &mut SyncIo, peer: PeerId) -> Result<(), network::Error> { - let warp_protocol_version = io.protocol_version(&WARP_SYNC_PROTOCOL_ID, peer); - let warp_protocol = warp_protocol_version != 0; - let protocol = if warp_protocol { warp_protocol_version } else { PROTOCOL_VERSION_63 }; - trace!(target: "sync", "Sending status to {}, protocol version {}", peer, protocol); - let mut packet = RlpStream::new_list(if warp_protocol { 7 } else { 5 }); - let chain = io.chain().chain_info(); - packet.append(&(protocol as u32)); - packet.append(&self.network_id); - packet.append(&chain.total_difficulty); - packet.append(&chain.best_block_hash); - packet.append(&chain.genesis_hash); - if warp_protocol { - let manifest = match self.old_blocks.is_some() { - true => None, - false => io.snapshot_service().manifest(), - }; - let block_number = manifest.as_ref().map_or(0, |m| m.block_number); - let manifest_hash = manifest.map_or(H256::new(), |m| keccak(m.into_rlp())); - packet.append(&manifest_hash); - packet.append(&block_number); - } - io.respond(STATUS_PACKET, packet.out()) - } - - /// Respond to GetBlockHeaders request - fn return_block_headers(io: &SyncIo, r: &UntrustedRlp, peer_id: PeerId) -> RlpResponseResult { - // Packet layout: - // [ block: { P , B_32 }, maxHeaders: P, skip: P, reverse: P in { 0 , 1 } ] - let max_headers: usize = r.val_at(1)?; - let skip: usize = r.val_at(2)?; - let reverse: bool = r.val_at(3)?; - let last = io.chain().chain_info().best_block_number; - let number = if r.at(0)?.size() == 32 { - // id is a hash - let hash: H256 = r.val_at(0)?; - trace!(target: "sync", "{} -> GetBlockHeaders (hash: {}, max: {}, skip: {}, reverse:{})", peer_id, hash, max_headers, skip, reverse); - match io.chain().block_header(BlockId::Hash(hash)) { - Some(hdr) => { - let number = hdr.number().into(); - debug_assert_eq!(hdr.hash(), hash); - - if max_headers == 1 || io.chain().block_hash(BlockId::Number(number)) != Some(hash) { - // Non canonical header or single header requested - // TODO: handle single-step reverse hashchains of non-canon hashes - trace!(target:"sync", "Returning single header: {:?}", hash); - let mut rlp = RlpStream::new_list(1); - rlp.append_raw(&hdr.into_inner(), 1); - return Ok(Some((BLOCK_HEADERS_PACKET, rlp))); - } - number - } - None => return Ok(Some((BLOCK_HEADERS_PACKET, RlpStream::new_list(0)))) //no such header, return nothing - } - } else { - trace!(target: "sync", "{} -> GetBlockHeaders (number: {}, max: {}, skip: {}, reverse:{})", peer_id, r.val_at::(0)?, max_headers, skip, reverse); - r.val_at(0)? - }; - - let mut number = if reverse { - cmp::min(last, number) - } else { - cmp::max(0, number) - }; - let max_count = cmp::min(MAX_HEADERS_TO_SEND, max_headers); - let mut count = 0; - let mut data = Bytes::new(); - let inc = (skip + 1) as BlockNumber; - let overlay = io.chain_overlay().read(); - - while number <= last && count < max_count { - if let Some(hdr) = overlay.get(&number) { - trace!(target: "sync", "{}: Returning cached fork header", peer_id); - data.extend_from_slice(hdr); - count += 1; - } else if let Some(hdr) = io.chain().block_header(BlockId::Number(number)) { - data.append(&mut hdr.into_inner()); - count += 1; - } else { - // No required block. - break; - } - if reverse { - if number <= inc || number == 0 { - break; - } - number -= inc; - } - else { - number += inc; - } - } - let mut rlp = RlpStream::new_list(count as usize); - rlp.append_raw(&data, count as usize); - trace!(target: "sync", "{} -> GetBlockHeaders: returned {} entries", peer_id, count); - Ok(Some((BLOCK_HEADERS_PACKET, rlp))) - } - - /// Respond to GetBlockBodies request - fn return_block_bodies(io: &SyncIo, r: &UntrustedRlp, peer_id: PeerId) -> RlpResponseResult { - let mut count = r.item_count().unwrap_or(0); - if count == 0 { - debug!(target: "sync", "Empty GetBlockBodies request, ignoring."); - return Ok(None); - } - count = cmp::min(count, MAX_BODIES_TO_SEND); - let mut added = 0usize; - let mut data = Bytes::new(); - for i in 0..count { - if let Some(body) = io.chain().block_body(BlockId::Hash(r.val_at::(i)?)) { - data.append(&mut body.into_inner()); - added += 1; - } - } - let mut rlp = RlpStream::new_list(added); - rlp.append_raw(&data, added); - trace!(target: "sync", "{} -> GetBlockBodies: returned {} entries", peer_id, added); - Ok(Some((BLOCK_BODIES_PACKET, rlp))) - } - - /// Respond to GetNodeData request - fn return_node_data(io: &SyncIo, r: &UntrustedRlp, peer_id: PeerId) -> RlpResponseResult { - let mut count = r.item_count().unwrap_or(0); - trace!(target: "sync", "{} -> GetNodeData: {} entries", peer_id, count); - if count == 0 { - debug!(target: "sync", "Empty GetNodeData request, ignoring."); - return Ok(None); - } - count = cmp::min(count, MAX_NODE_DATA_TO_SEND); - let mut added = 0usize; - let mut data = Vec::new(); - for i in 0..count { - if let Some(node) = io.chain().state_data(&r.val_at::(i)?) { - data.push(node); - added += 1; - } - } - trace!(target: "sync", "{} -> GetNodeData: return {} entries", peer_id, added); - let mut rlp = RlpStream::new_list(added); - for d in data { - rlp.append(&d); - } - Ok(Some((NODE_DATA_PACKET, rlp))) - } - - fn return_receipts(io: &SyncIo, rlp: &UntrustedRlp, peer_id: PeerId) -> RlpResponseResult { - let mut count = rlp.item_count().unwrap_or(0); - trace!(target: "sync", "{} -> GetReceipts: {} entries", peer_id, count); - if count == 0 { - debug!(target: "sync", "Empty GetReceipts request, ignoring."); - return Ok(None); - } - count = cmp::min(count, MAX_RECEIPTS_HEADERS_TO_SEND); - let mut added_headers = 0usize; - let mut added_receipts = 0usize; - let mut data = Bytes::new(); - for i in 0..count { - if let Some(mut receipts_bytes) = io.chain().block_receipts(&rlp.val_at::(i)?) { - data.append(&mut receipts_bytes); - added_receipts += receipts_bytes.len(); - added_headers += 1; - if added_receipts > MAX_RECEIPTS_TO_SEND { break; } - } - } - let mut rlp_result = RlpStream::new_list(added_headers); - rlp_result.append_raw(&data, added_headers); - Ok(Some((RECEIPTS_PACKET, rlp_result))) - } - - /// Respond to GetSnapshotManifest request - fn return_snapshot_manifest(io: &SyncIo, r: &UntrustedRlp, peer_id: PeerId) -> RlpResponseResult { - let count = r.item_count().unwrap_or(0); - trace!(target: "sync", "{} -> GetSnapshotManifest", peer_id); - if count != 0 { - debug!(target: "sync", "Invalid GetSnapshotManifest request, ignoring."); - return Ok(None); - } - let rlp = match io.snapshot_service().manifest() { - Some(manifest) => { - trace!(target: "sync", "{} <- SnapshotManifest", peer_id); - let mut rlp = RlpStream::new_list(1); - rlp.append_raw(&manifest.into_rlp(), 1); - rlp - }, - None => { - trace!(target: "sync", "{}: No manifest to return", peer_id); - RlpStream::new_list(0) - } - }; - Ok(Some((SNAPSHOT_MANIFEST_PACKET, rlp))) - } - - /// Respond to GetSnapshotData request - fn return_snapshot_data(io: &SyncIo, r: &UntrustedRlp, peer_id: PeerId) -> RlpResponseResult { - let hash: H256 = r.val_at(0)?; - trace!(target: "sync", "{} -> GetSnapshotData {:?}", peer_id, hash); - let rlp = match io.snapshot_service().chunk(hash) { - Some(data) => { - let mut rlp = RlpStream::new_list(1); - trace!(target: "sync", "{} <- SnapshotData", peer_id); - rlp.append(&data); - rlp - }, - None => { - RlpStream::new_list(0) - } - }; - Ok(Some((SNAPSHOT_DATA_PACKET, rlp))) - } - - fn return_rlp(io: &mut SyncIo, rlp: &UntrustedRlp, peer: PeerId, rlp_func: FRlp, error_func: FError) -> Result<(), PacketDecodeError> - where FRlp : Fn(&SyncIo, &UntrustedRlp, PeerId) -> RlpResponseResult, - FError : FnOnce(network::Error) -> String - { - let response = rlp_func(io, rlp, peer); - match response { - Err(e) => Err(e), - Ok(Some((packet_id, rlp_stream))) => { - io.respond(packet_id, rlp_stream.out()).unwrap_or_else( - |e| debug!(target: "sync", "{:?}", error_func(e))); - Ok(()) - } - _ => Ok(()) - } - } - - /// Dispatch incoming requests and responses - pub fn dispatch_packet(sync: &RwLock, io: &mut SyncIo, peer: PeerId, packet_id: u8, data: &[u8]) { - let rlp = UntrustedRlp::new(data); - let result = match packet_id { - GET_BLOCK_BODIES_PACKET => ChainSync::return_rlp(io, &rlp, peer, - ChainSync::return_block_bodies, - |e| format!("Error sending block bodies: {:?}", e)), - - GET_BLOCK_HEADERS_PACKET => ChainSync::return_rlp(io, &rlp, peer, - ChainSync::return_block_headers, - |e| format!("Error sending block headers: {:?}", e)), - - GET_RECEIPTS_PACKET => ChainSync::return_rlp(io, &rlp, peer, - ChainSync::return_receipts, - |e| format!("Error sending receipts: {:?}", e)), - - GET_NODE_DATA_PACKET => ChainSync::return_rlp(io, &rlp, peer, - ChainSync::return_node_data, - |e| format!("Error sending nodes: {:?}", e)), - - GET_SNAPSHOT_MANIFEST_PACKET => ChainSync::return_rlp(io, &rlp, peer, - ChainSync::return_snapshot_manifest, - |e| format!("Error sending snapshot manifest: {:?}", e)), - - GET_SNAPSHOT_DATA_PACKET => ChainSync::return_rlp(io, &rlp, peer, - ChainSync::return_snapshot_data, - |e| format!("Error sending snapshot data: {:?}", e)), - CONSENSUS_DATA_PACKET => ChainSync::on_consensus_packet(io, peer, &rlp), - _ => { - sync.write().on_packet(io, peer, packet_id, data); - Ok(()) - } - }; - result.unwrap_or_else(|e| { - debug!(target:"sync", "{} -> Malformed packet {} : {}", peer, packet_id, e); - }) - } - - pub fn on_packet(&mut self, io: &mut SyncIo, peer: PeerId, packet_id: u8, data: &[u8]) { - if packet_id != STATUS_PACKET && !self.peers.contains_key(&peer) { - debug!(target:"sync", "Unexpected packet {} from unregistered peer: {}:{}", packet_id, peer, io.peer_info(peer)); - return; - } - let rlp = UntrustedRlp::new(data); - let result = match packet_id { - STATUS_PACKET => self.on_peer_status(io, peer, &rlp), - TRANSACTIONS_PACKET => self.on_peer_transactions(io, peer, &rlp), - BLOCK_HEADERS_PACKET => self.on_peer_block_headers(io, peer, &rlp), - BLOCK_BODIES_PACKET => self.on_peer_block_bodies(io, peer, &rlp), - RECEIPTS_PACKET => self.on_peer_block_receipts(io, peer, &rlp), - NEW_BLOCK_PACKET => self.on_peer_new_block(io, peer, &rlp), - NEW_BLOCK_HASHES_PACKET => self.on_peer_new_hashes(io, peer, &rlp), - SNAPSHOT_MANIFEST_PACKET => self.on_snapshot_manifest(io, peer, &rlp), - SNAPSHOT_DATA_PACKET => self.on_snapshot_data(io, peer, &rlp), - _ => { - debug!(target: "sync", "{}: Unknown packet {}", peer, packet_id); - Ok(()) - } - }; - result.unwrap_or_else(|e| { - debug!(target:"sync", "{} -> Malformed packet {} : {}", peer, packet_id, e); - }) - } - - pub fn maintain_peers(&mut self, io: &mut SyncIo) { - let tick = Instant::now(); - let mut aborting = Vec::new(); - for (peer_id, peer) in &self.peers { - let elapsed = (tick - peer.ask_time).as_secs(); - let timeout = match peer.asking { - PeerAsking::BlockHeaders => elapsed > HEADERS_TIMEOUT_SEC, - PeerAsking::BlockBodies => elapsed > BODIES_TIMEOUT_SEC, - PeerAsking::BlockReceipts => elapsed > RECEIPTS_TIMEOUT_SEC, - PeerAsking::Nothing => false, - PeerAsking::ForkHeader => elapsed > FORK_HEADER_TIMEOUT_SEC, - PeerAsking::SnapshotManifest => elapsed > SNAPSHOT_MANIFEST_TIMEOUT_SEC, - PeerAsking::SnapshotData => elapsed > SNAPSHOT_DATA_TIMEOUT_SEC, - }; - if timeout { - trace!(target:"sync", "Timeout {}", peer_id); - io.disconnect_peer(*peer_id); - aborting.push(*peer_id); - } - } - for p in aborting { - self.on_peer_aborting(io, p); - } - - // Check for handshake timeouts - for (peer, &ask_time) in &self.handshaking_peers { - let elapsed = (tick - ask_time) / 1_000_000_000; - if elapsed.as_secs() > STATUS_TIMEOUT_SEC { - trace!(target:"sync", "Status timeout {}", peer); - io.disconnect_peer(*peer); - } - } - } - - fn check_resume(&mut self, io: &mut SyncIo) { - if self.state == SyncState::Waiting && !io.chain().queue_info().is_full() && self.state == SyncState::Waiting { - self.state = SyncState::Blocks; - self.continue_sync(io); - } else if self.state == SyncState::SnapshotWaiting { - match io.snapshot_service().status() { - RestorationStatus::Inactive => { - trace!(target:"sync", "Snapshot restoration is complete"); - self.restart(io); - self.continue_sync(io); - }, - RestorationStatus::Ongoing { state_chunks_done, block_chunks_done, .. } => { - if !self.snapshot.is_complete() && self.snapshot.done_chunks() - (state_chunks_done + block_chunks_done) as usize <= MAX_SNAPSHOT_CHUNKS_DOWNLOAD_AHEAD { - trace!(target:"sync", "Resuming snapshot sync"); - self.state = SyncState::SnapshotData; - self.continue_sync(io); - } - }, - RestorationStatus::Failed => { - trace!(target: "sync", "Snapshot restoration aborted"); - self.state = SyncState::WaitingPeers; - self.snapshot.clear(); - self.continue_sync(io); - }, - } - } - } - - /// creates rlp to send for the tree defined by 'from' and 'to' hashes - fn create_new_hashes_rlp(chain: &BlockChainClient, from: &H256, to: &H256) -> Option { - match chain.tree_route(from, to) { - Some(route) => { - let uncles = chain.find_uncles(from).unwrap_or_else(Vec::new); - match route.blocks.len() { - 0 => None, - _ => { - let mut blocks = route.blocks; - blocks.extend(uncles); - let mut rlp_stream = RlpStream::new_list(blocks.len()); - for block_hash in blocks { - let mut hash_rlp = RlpStream::new_list(2); - let number = chain.block_header(BlockId::Hash(block_hash.clone())) - .expect("chain.tree_route and chain.find_uncles only return hahses of blocks that are in the blockchain. qed.").number(); - hash_rlp.append(&block_hash); - hash_rlp.append(&number); - rlp_stream.append_raw(hash_rlp.as_raw(), 1); - } - Some(rlp_stream.out()) - } - } - }, - None => None - } - } - - /// creates rlp from block bytes and total difficulty - fn create_block_rlp(bytes: &Bytes, total_difficulty: U256) -> Bytes { - let mut rlp_stream = RlpStream::new_list(2); - rlp_stream.append_raw(bytes, 1); - rlp_stream.append(&total_difficulty); - rlp_stream.out() - } - - /// creates latest block rlp for the given client - fn create_latest_block_rlp(chain: &BlockChainClient) -> Bytes { - ChainSync::create_block_rlp( - &chain.block(BlockId::Hash(chain.chain_info().best_block_hash)) - .expect("Best block always exists").into_inner(), - chain.chain_info().total_difficulty - ) - } - - /// creates given hash block rlp for the given client - fn create_new_block_rlp(chain: &BlockChainClient, hash: &H256) -> Bytes { - ChainSync::create_block_rlp( - &chain.block(BlockId::Hash(hash.clone())).expect("Block has just been sealed; qed").into_inner(), - chain.block_total_difficulty(BlockId::Hash(hash.clone())).expect("Block has just been sealed; qed.") - ) - } - - /// returns peer ids that have different blocks than our chain - fn get_lagging_peers(&mut self, chain_info: &BlockChainInfo) -> Vec { - let latest_hash = chain_info.best_block_hash; - self - .peers - .iter_mut() - .filter_map(|(&id, ref mut peer_info)| { - trace!(target: "sync", "Checking peer our best {} their best {}", latest_hash, peer_info.latest_hash); - if peer_info.latest_hash != latest_hash { - Some(id) - } else { - None - } - }) - .collect::>() - } - - fn select_random_peers(peers: &[PeerId]) -> Vec { - // take sqrt(x) peers - let mut peers = peers.to_vec(); - let mut count = (peers.len() as f64).powf(0.5).round() as usize; - count = cmp::min(count, MAX_PEERS_PROPAGATION); - count = cmp::max(count, MIN_PEERS_PROPAGATION); - random::new().shuffle(&mut peers); - peers.truncate(count); - peers - } - - fn get_consensus_peers(&self) -> Vec { - self.peers.iter().filter_map(|(id, p)| if p.protocol_version == PROTOCOL_VERSION_2 { Some(*id) } else { None }).collect() - } - - /// propagates latest block to a set of peers - fn propagate_blocks(&mut self, chain_info: &BlockChainInfo, io: &mut SyncIo, blocks: &[H256], peers: &[PeerId]) -> usize { - trace!(target: "sync", "Sending NewBlocks to {:?}", peers); - let mut sent = 0; - for peer_id in peers { - if blocks.is_empty() { - let rlp = ChainSync::create_latest_block_rlp(io.chain()); - self.send_packet(io, *peer_id, NEW_BLOCK_PACKET, rlp); - } else { - for h in blocks { - let rlp = ChainSync::create_new_block_rlp(io.chain(), h); - self.send_packet(io, *peer_id, NEW_BLOCK_PACKET, rlp); - } - } - if let Some(ref mut peer) = self.peers.get_mut(peer_id) { - peer.latest_hash = chain_info.best_block_hash.clone(); - } - sent += 1; - } - sent - } - - /// propagates new known hashes to all peers - fn propagate_new_hashes(&mut self, chain_info: &BlockChainInfo, io: &mut SyncIo, peers: &[PeerId]) -> usize { - trace!(target: "sync", "Sending NewHashes to {:?}", peers); - let mut sent = 0; - let last_parent = &io.chain().best_block_header().parent_hash(); - for peer_id in peers { - sent += match ChainSync::create_new_hashes_rlp(io.chain(), &last_parent, &chain_info.best_block_hash) { - Some(rlp) => { - { - if let Some(ref mut peer) = self.peers.get_mut(peer_id) { - peer.latest_hash = chain_info.best_block_hash.clone(); - } - } - self.send_packet(io, *peer_id, NEW_BLOCK_HASHES_PACKET, rlp); - 1 - }, - None => 0 - } - } - sent - } - - /// propagates new transactions to all peers - pub fn propagate_new_transactions(&mut self, io: &mut SyncIo) -> usize { - // Early out if nobody to send to. - if self.peers.is_empty() { - return 0; - } - - let transactions = io.chain().ready_transactions(); - if transactions.is_empty() { - return 0; - } - - let (transactions, service_transactions): (Vec<_>, Vec<_>) = transactions.into_iter() - .partition(|tx| !tx.transaction.gas_price.is_zero()); - - // usual transactions could be propagated to all peers - let mut affected_peers = HashSet::new(); - if !transactions.is_empty() { - let peers = self.select_peers_for_transactions(|_| true); - affected_peers = self.propagate_transactions_to_peers(io, peers, transactions); - } - - // most of times service_transactions will be empty - // => there's no need to merge packets - if !service_transactions.is_empty() { - let service_transactions_peers = self.select_peers_for_transactions(|peer_id| accepts_service_transaction(&io.peer_info(*peer_id))); - let service_transactions_affected_peers = self.propagate_transactions_to_peers(io, service_transactions_peers, service_transactions); - affected_peers.extend(&service_transactions_affected_peers); - } - - affected_peers.len() - } - - fn select_peers_for_transactions(&self, filter: F) -> Vec - where F: Fn(&PeerId) -> bool { - // sqrt(x)/x scaled to max u32 - let fraction = ((self.peers.len() as f64).powf(-0.5) * (u32::max_value() as f64).round()) as u32; - let small = self.peers.len() < MIN_PEERS_PROPAGATION; - - let mut random = random::new(); - self.peers.keys() - .cloned() - .filter(filter) - .filter(|_| small || random.next_u32() < fraction) - .take(MAX_PEERS_PROPAGATION) - .collect() - } - - fn propagate_transactions_to_peers(&mut self, io: &mut SyncIo, peers: Vec, transactions: Vec) -> HashSet { - let all_transactions_hashes = transactions.iter() - .map(|tx| tx.transaction.hash()) - .collect::>(); - let all_transactions_rlp = { - let mut packet = RlpStream::new_list(transactions.len()); - for tx in &transactions { packet.append(&tx.transaction); } - packet.out() - }; - - // Clear old transactions from stats - self.transactions_stats.retain(&all_transactions_hashes); - - // sqrt(x)/x scaled to max u32 - let block_number = io.chain().chain_info().best_block_number; - - let lucky_peers = { - peers.into_iter() - .filter_map(|peer_id| { - let stats = &mut self.transactions_stats; - let peer_info = self.peers.get_mut(&peer_id) - .expect("peer_id is form peers; peers is result of select_peers_for_transactions; select_peers_for_transactions selects peers from self.peers; qed"); - - // Send all transactions - if peer_info.last_sent_transactions.is_empty() { - // update stats - for hash in &all_transactions_hashes { - let id = io.peer_session_info(peer_id).and_then(|info| info.id); - stats.propagated(hash, id, block_number); - } - peer_info.last_sent_transactions = all_transactions_hashes.clone(); - return Some((peer_id, all_transactions_hashes.len(), all_transactions_rlp.clone())); - } - - // Get hashes of all transactions to send to this peer - let to_send = all_transactions_hashes.difference(&peer_info.last_sent_transactions) - .take(MAX_TRANSACTIONS_TO_PROPAGATE) - .cloned() - .collect::>(); - if to_send.is_empty() { - return None; - } - - // Construct RLP - let (packet, to_send) = { - let mut to_send = to_send; - let mut packet = RlpStream::new(); - packet.begin_unbounded_list(); - let mut pushed = 0; - for tx in &transactions { - let hash = tx.transaction.hash(); - if to_send.contains(&hash) { - let mut transaction = RlpStream::new(); - tx.transaction.rlp_append(&mut transaction); - let appended = packet.append_raw_checked(&transaction.drain(), 1, MAX_TRANSACTION_PACKET_SIZE); - if !appended { - // Maximal packet size reached just proceed with sending - debug!("Transaction packet size limit reached. Sending incomplete set of {}/{} transactions.", pushed, to_send.len()); - to_send = to_send.into_iter().take(pushed).collect(); - break; - } - pushed += 1; - } - } - packet.complete_unbounded_list(); - (packet, to_send) - }; - - // Update stats - let id = io.peer_session_info(peer_id).and_then(|info| info.id); - for hash in &to_send { - // update stats - stats.propagated(hash, id, block_number); - } - - peer_info.last_sent_transactions = all_transactions_hashes - .intersection(&peer_info.last_sent_transactions) - .chain(&to_send) - .cloned() - .collect(); - Some((peer_id, to_send.len(), packet.out())) - }) - .collect::>() - }; - - // Send RLPs - let mut peers = HashSet::new(); - if lucky_peers.len() > 0 { - let mut max_sent = 0; - let lucky_peers_len = lucky_peers.len(); - for (peer_id, sent, rlp) in lucky_peers { - peers.insert(peer_id); - self.send_packet(io, peer_id, TRANSACTIONS_PACKET, rlp); - trace!(target: "sync", "{:02} <- Transactions ({} entries)", peer_id, sent); - max_sent = cmp::max(max_sent, sent); - } - debug!(target: "sync", "Sent up to {} transactions to {} peers.", max_sent, lucky_peers_len); - } - - peers - } - - fn propagate_latest_blocks(&mut self, io: &mut SyncIo, sealed: &[H256]) { - let chain_info = io.chain().chain_info(); - if (((chain_info.best_block_number as i64) - (self.last_sent_block_number as i64)).abs() as BlockNumber) < MAX_PEER_LAG_PROPAGATION { - let mut peers = self.get_lagging_peers(&chain_info); - if sealed.is_empty() { - let hashes = self.propagate_new_hashes(&chain_info, io, &peers); - peers = ChainSync::select_random_peers(&peers); - let blocks = self.propagate_blocks(&chain_info, io, sealed, &peers); - if blocks != 0 || hashes != 0 { - trace!(target: "sync", "Sent latest {} blocks and {} hashes to peers.", blocks, hashes); - } - } else { - self.propagate_blocks(&chain_info, io, sealed, &peers); - self.propagate_new_hashes(&chain_info, io, &peers); - trace!(target: "sync", "Sent sealed block to all peers"); - }; - } - self.last_sent_block_number = chain_info.best_block_number; - } - - /// Distribute valid proposed blocks to subset of current peers. - fn propagate_proposed_blocks(&mut self, io: &mut SyncIo, proposed: &[Bytes]) { - let peers = self.get_consensus_peers(); - trace!(target: "sync", "Sending proposed blocks to {:?}", peers); - for block in proposed { - let rlp = ChainSync::create_block_rlp( - block, - io.chain().chain_info().total_difficulty - ); - for peer_id in &peers { - self.send_packet(io, *peer_id, NEW_BLOCK_PACKET, rlp.clone()); - } - } - } - - /// Maintain other peers. Send out any new blocks and transactions - pub fn maintain_sync(&mut self, io: &mut SyncIo) { - self.maybe_start_snapshot_sync(io); - self.check_resume(io); - } - - /// called when block is imported to chain - propagates the blocks and updates transactions sent to peers - pub fn chain_new_blocks(&mut self, io: &mut SyncIo, _imported: &[H256], invalid: &[H256], enacted: &[H256], _retracted: &[H256], sealed: &[H256], proposed: &[Bytes]) { - let queue_info = io.chain().queue_info(); - let is_syncing = self.status().is_syncing(queue_info); - - if !is_syncing || !sealed.is_empty() || !proposed.is_empty() { - trace!(target: "sync", "Propagating blocks, state={:?}", self.state); - self.propagate_latest_blocks(io, sealed); - self.propagate_proposed_blocks(io, proposed); - } - if !invalid.is_empty() { - trace!(target: "sync", "Bad blocks in the queue, restarting"); - self.restart(io); - } - - if !is_syncing && !enacted.is_empty() && !self.peers.is_empty() { - // Select random peer to re-broadcast transactions to. - let peer = random::new().gen_range(0, self.peers.len()); - trace!(target: "sync", "Re-broadcasting transactions to a random peer."); - self.peers.values_mut().nth(peer).map(|peer_info| - peer_info.last_sent_transactions.clear() - ); - } - } - - /// Called when peer sends us new consensus packet - fn on_consensus_packet(io: &mut SyncIo, peer_id: PeerId, r: &UntrustedRlp) -> Result<(), PacketDecodeError> { - trace!(target: "sync", "Received consensus packet from {:?}", peer_id); - io.chain().queue_consensus_message(r.as_raw().to_vec()); - Ok(()) - } - - /// Broadcast consensus message to peers. - pub fn propagate_consensus_packet(&mut self, io: &mut SyncIo, packet: Bytes) { - let lucky_peers = ChainSync::select_random_peers(&self.get_consensus_peers()); - trace!(target: "sync", "Sending consensus packet to {:?}", lucky_peers); - for peer_id in lucky_peers { - self.send_packet(io, peer_id, CONSENSUS_DATA_PACKET, packet.clone()); - } - } -} - -/// Checks if peer is able to process service transactions -fn accepts_service_transaction(client_id: &str) -> bool { - // Parity versions starting from this will accept service-transactions - const SERVICE_TRANSACTIONS_VERSION: (u32, u32) = (1u32, 6u32); - // Parity client string prefix - const PARITY_CLIENT_ID_PREFIX: &'static str = "Parity/v"; - - if !client_id.starts_with(PARITY_CLIENT_ID_PREFIX) { - return false; - } - let ver: Vec = client_id[PARITY_CLIENT_ID_PREFIX.len()..].split('.') - .take(2) - .filter_map(|s| s.parse().ok()) - .collect(); - ver.len() == 2 && (ver[0] > SERVICE_TRANSACTIONS_VERSION.0 || (ver[0] == SERVICE_TRANSACTIONS_VERSION.0 && ver[1] >= SERVICE_TRANSACTIONS_VERSION.1)) -} - -#[cfg(test)] -mod tests { - use std::collections::{HashSet, VecDeque}; - use ethkey; - use network::PeerId; - use tests::helpers::*; - use tests::snapshot::TestSnapshotService; - use ethereum_types::{H256, U256, Address}; - use parking_lot::RwLock; - use bytes::Bytes; - use rlp::{Rlp, RlpStream, UntrustedRlp}; - use super::*; - use ::SyncConfig; - use super::{PeerInfo, PeerAsking}; - use ethcore::header::*; - use ethcore::client::{BlockChainClient, EachBlockWith, TestBlockChainClient, ChainInfo, BlockInfo}; - use ethcore::miner::MinerService; - use transaction::UnverifiedTransaction; - - fn get_dummy_block(order: u32, parent_hash: H256) -> Bytes { - let mut header = Header::new(); - header.set_gas_limit(0.into()); - header.set_difficulty((order * 100).into()); - header.set_timestamp((order * 10) as u64); - header.set_number(order as u64); - header.set_parent_hash(parent_hash); - header.set_state_root(H256::zero()); - - let mut rlp = RlpStream::new_list(3); - rlp.append(&header); - rlp.append_raw(&::rlp::EMPTY_LIST_RLP, 1); - rlp.append_raw(&::rlp::EMPTY_LIST_RLP, 1); - rlp.out() - } - - fn get_dummy_blocks(order: u32, parent_hash: H256) -> Bytes { - let mut rlp = RlpStream::new_list(1); - rlp.append_raw(&get_dummy_block(order, parent_hash), 1); - let difficulty: U256 = (100 * order).into(); - rlp.append(&difficulty); - rlp.out() - } - - fn get_dummy_hashes() -> Bytes { - let mut rlp = RlpStream::new_list(5); - for _ in 0..5 { - let mut hash_d_rlp = RlpStream::new_list(2); - let hash: H256 = H256::from(0u64); - let diff: U256 = U256::from(1u64); - hash_d_rlp.append(&hash); - hash_d_rlp.append(&diff); - - rlp.append_raw(&hash_d_rlp.out(), 1); - } - - rlp.out() - } - - fn queue_info(unverified: usize, verified: usize) -> BlockQueueInfo { - BlockQueueInfo { - unverified_queue_size: unverified, - verified_queue_size: verified, - verifying_queue_size: 0, - max_queue_size: 1000, - max_mem_use: 1000, - mem_used: 500 - } - } - - fn sync_status(state: SyncState) -> SyncStatus { - SyncStatus { - state: state, - protocol_version: 0, - network_id: 0, - start_block_number: 0, - last_imported_block_number: None, - highest_block_number: None, - blocks_total: 0, - blocks_received: 0, - num_peers: 0, - num_active_peers: 0, - mem_used: 0, - num_snapshot_chunks: 0, - snapshot_chunks_done: 0, - last_imported_old_block_number: None, - } - } - - #[test] - fn is_still_verifying() { - assert!(!sync_status(SyncState::Idle).is_syncing(queue_info(2, 1))); - assert!(sync_status(SyncState::Idle).is_syncing(queue_info(2, 2))); - } - - #[test] - fn is_synced_state() { - assert!(sync_status(SyncState::Blocks).is_syncing(queue_info(0, 0))); - assert!(!sync_status(SyncState::Idle).is_syncing(queue_info(0, 0))); - } - - #[test] - fn return_receipts_empty() { - let mut client = TestBlockChainClient::new(); - let queue = RwLock::new(VecDeque::new()); - let ss = TestSnapshotService::new(); - let io = TestIo::new(&mut client, &ss, &queue, None); - - let result = ChainSync::return_receipts(&io, &UntrustedRlp::new(&[0xc0]), 0); - - assert!(result.is_ok()); - } - - #[test] - fn return_receipts() { - let mut client = TestBlockChainClient::new(); - let queue = RwLock::new(VecDeque::new()); - let sync = dummy_sync_with_peer(H256::new(), &client); - let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); - - let mut receipt_list = RlpStream::new_list(4); - receipt_list.append(&H256::from("0000000000000000000000000000000000000000000000005555555555555555")); - receipt_list.append(&H256::from("ff00000000000000000000000000000000000000000000000000000000000000")); - receipt_list.append(&H256::from("fff0000000000000000000000000000000000000000000000000000000000000")); - receipt_list.append(&H256::from("aff0000000000000000000000000000000000000000000000000000000000000")); - - let receipts_request = receipt_list.out(); - // it returns rlp ONLY for hashes started with "f" - let result = ChainSync::return_receipts(&io, &UntrustedRlp::new(&receipts_request.clone()), 0); - - assert!(result.is_ok()); - let rlp_result = result.unwrap(); - assert!(rlp_result.is_some()); - - // the length of two rlp-encoded receipts - assert_eq!(603, rlp_result.unwrap().1.out().len()); - - io.sender = Some(2usize); - ChainSync::dispatch_packet(&RwLock::new(sync), &mut io, 0usize, super::GET_RECEIPTS_PACKET, &receipts_request); - assert_eq!(1, io.packets.len()); - } - - #[test] - fn return_block_headers() { - use ethcore::views::HeaderView; - fn make_hash_req(h: &H256, count: usize, skip: usize, reverse: bool) -> Bytes { - let mut rlp = RlpStream::new_list(4); - rlp.append(h); - rlp.append(&count); - rlp.append(&skip); - rlp.append(&if reverse {1u32} else {0u32}); - rlp.out() - } - - fn make_num_req(n: usize, count: usize, skip: usize, reverse: bool) -> Bytes { - let mut rlp = RlpStream::new_list(4); - rlp.append(&n); - rlp.append(&count); - rlp.append(&skip); - rlp.append(&if reverse {1u32} else {0u32}); - rlp.out() - } - fn to_header_vec(rlp: ::chain::RlpResponseResult) -> Vec { - Rlp::new(&rlp.unwrap().unwrap().1.out()).iter().map(|r| r.as_raw().to_vec()).collect() - } - - let mut client = TestBlockChainClient::new(); - client.add_blocks(100, EachBlockWith::Nothing); - let blocks: Vec<_> = (0 .. 100) - .map(|i| (&client as &BlockChainClient).block(BlockId::Number(i as BlockNumber)).map(|b| b.into_inner()).unwrap()).collect(); - let headers: Vec<_> = blocks.iter().map(|b| Rlp::new(b).at(0).as_raw().to_vec()).collect(); - let hashes: Vec<_> = headers.iter().map(|h| HeaderView::new(h).hash()).collect(); - - let queue = RwLock::new(VecDeque::new()); - let ss = TestSnapshotService::new(); - let io = TestIo::new(&mut client, &ss, &queue, None); - - let unknown: H256 = H256::new(); - let result = ChainSync::return_block_headers(&io, &UntrustedRlp::new(&make_hash_req(&unknown, 1, 0, false)), 0); - assert!(to_header_vec(result).is_empty()); - let result = ChainSync::return_block_headers(&io, &UntrustedRlp::new(&make_hash_req(&unknown, 1, 0, true)), 0); - assert!(to_header_vec(result).is_empty()); - - let result = ChainSync::return_block_headers(&io, &UntrustedRlp::new(&make_hash_req(&hashes[2], 1, 0, true)), 0); - assert_eq!(to_header_vec(result), vec![headers[2].clone()]); - - let result = ChainSync::return_block_headers(&io, &UntrustedRlp::new(&make_hash_req(&hashes[2], 1, 0, false)), 0); - assert_eq!(to_header_vec(result), vec![headers[2].clone()]); - - let result = ChainSync::return_block_headers(&io, &UntrustedRlp::new(&make_hash_req(&hashes[50], 3, 5, false)), 0); - assert_eq!(to_header_vec(result), vec![headers[50].clone(), headers[56].clone(), headers[62].clone()]); - - let result = ChainSync::return_block_headers(&io, &UntrustedRlp::new(&make_hash_req(&hashes[50], 3, 5, true)), 0); - assert_eq!(to_header_vec(result), vec![headers[50].clone(), headers[44].clone(), headers[38].clone()]); - - let result = ChainSync::return_block_headers(&io, &UntrustedRlp::new(&make_num_req(2, 1, 0, true)), 0); - assert_eq!(to_header_vec(result), vec![headers[2].clone()]); - - let result = ChainSync::return_block_headers(&io, &UntrustedRlp::new(&make_num_req(2, 1, 0, false)), 0); - assert_eq!(to_header_vec(result), vec![headers[2].clone()]); - - let result = ChainSync::return_block_headers(&io, &UntrustedRlp::new(&make_num_req(50, 3, 5, false)), 0); - assert_eq!(to_header_vec(result), vec![headers[50].clone(), headers[56].clone(), headers[62].clone()]); - - let result = ChainSync::return_block_headers(&io, &UntrustedRlp::new(&make_num_req(50, 3, 5, true)), 0); - assert_eq!(to_header_vec(result), vec![headers[50].clone(), headers[44].clone(), headers[38].clone()]); - } - - #[test] - fn return_nodes() { - let mut client = TestBlockChainClient::new(); - let queue = RwLock::new(VecDeque::new()); - let sync = dummy_sync_with_peer(H256::new(), &client); - let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); - - let mut node_list = RlpStream::new_list(3); - node_list.append(&H256::from("0000000000000000000000000000000000000000000000005555555555555555")); - node_list.append(&H256::from("ffffffffffffffffffffffffffffffffffffffffffffaaaaaaaaaaaaaaaaaaaa")); - node_list.append(&H256::from("aff0000000000000000000000000000000000000000000000000000000000000")); - - let node_request = node_list.out(); - // it returns rlp ONLY for hashes started with "f" - let result = ChainSync::return_node_data(&io, &UntrustedRlp::new(&node_request.clone()), 0); - - assert!(result.is_ok()); - let rlp_result = result.unwrap(); - assert!(rlp_result.is_some()); - - // the length of one rlp-encoded hashe - let rlp = rlp_result.unwrap().1.out(); - let rlp = Rlp::new(&rlp); - assert_eq!(1, rlp.item_count()); - - io.sender = Some(2usize); - - ChainSync::dispatch_packet(&RwLock::new(sync), &mut io, 0usize, super::GET_NODE_DATA_PACKET, &node_request); - assert_eq!(1, io.packets.len()); - } - - fn dummy_sync_with_peer(peer_latest_hash: H256, client: &BlockChainClient) -> ChainSync { - let mut sync = ChainSync::new(SyncConfig::default(), client); - insert_dummy_peer(&mut sync, 0, peer_latest_hash); - sync - } - - fn insert_dummy_peer(sync: &mut ChainSync, peer_id: PeerId, peer_latest_hash: H256) { - sync.peers.insert(peer_id, - PeerInfo { - protocol_version: 0, - genesis: H256::zero(), - network_id: 0, - latest_hash: peer_latest_hash, - difficulty: None, - asking: PeerAsking::Nothing, - asking_blocks: Vec::new(), - asking_hash: None, - ask_time: Instant::now(), - last_sent_transactions: HashSet::new(), - expired: false, - confirmation: super::ForkConfirmation::Confirmed, - snapshot_number: None, - snapshot_hash: None, - asking_snapshot_data: None, - block_set: None, - }); - - } - - #[test] - fn finds_lagging_peers() { - let mut client = TestBlockChainClient::new(); - client.add_blocks(100, EachBlockWith::Uncle); - let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(10), &client); - let chain_info = client.chain_info(); - - let lagging_peers = sync.get_lagging_peers(&chain_info); - - assert_eq!(1, lagging_peers.len()); - } - - #[test] - fn calculates_tree_for_lagging_peer() { - let mut client = TestBlockChainClient::new(); - client.add_blocks(15, EachBlockWith::Uncle); - - let start = client.block_hash_delta_minus(4); - let end = client.block_hash_delta_minus(2); - - // wrong way end -> start, should be None - let rlp = ChainSync::create_new_hashes_rlp(&client, &end, &start); - assert!(rlp.is_none()); - - let rlp = ChainSync::create_new_hashes_rlp(&client, &start, &end).unwrap(); - // size of three rlp encoded hash-difficulty - assert_eq!(107, rlp.len()); - } - - #[test] - fn sends_new_hashes_to_lagging_peer() { - let mut client = TestBlockChainClient::new(); - client.add_blocks(100, EachBlockWith::Uncle); - let queue = RwLock::new(VecDeque::new()); - let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); - let chain_info = client.chain_info(); - let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); - - let peers = sync.get_lagging_peers(&chain_info); - let peer_count = sync.propagate_new_hashes(&chain_info, &mut io, &peers); - - // 1 message should be send - assert_eq!(1, io.packets.len()); - // 1 peer should be updated - assert_eq!(1, peer_count); - // NEW_BLOCK_HASHES_PACKET - assert_eq!(0x01, io.packets[0].packet_id); - } - - #[test] - fn sends_latest_block_to_lagging_peer() { - let mut client = TestBlockChainClient::new(); - client.add_blocks(100, EachBlockWith::Uncle); - let queue = RwLock::new(VecDeque::new()); - let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); - let chain_info = client.chain_info(); - let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); - let peers = sync.get_lagging_peers(&chain_info); - let peer_count = sync.propagate_blocks(&chain_info, &mut io, &[], &peers); - - // 1 message should be send - assert_eq!(1, io.packets.len()); - // 1 peer should be updated - assert_eq!(1, peer_count); - // NEW_BLOCK_PACKET - assert_eq!(0x07, io.packets[0].packet_id); - } - - #[test] - fn sends_sealed_block() { - let mut client = TestBlockChainClient::new(); - client.add_blocks(100, EachBlockWith::Uncle); - let queue = RwLock::new(VecDeque::new()); - let hash = client.block_hash(BlockId::Number(99)).unwrap(); - let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); - let chain_info = client.chain_info(); - let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); - let peers = sync.get_lagging_peers(&chain_info); - let peer_count = sync.propagate_blocks(&chain_info, &mut io, &[hash.clone()], &peers); - - // 1 message should be send - assert_eq!(1, io.packets.len()); - // 1 peer should be updated - assert_eq!(1, peer_count); - // NEW_BLOCK_PACKET - assert_eq!(0x07, io.packets[0].packet_id); - } - - #[test] - fn sends_proposed_block() { - let mut client = TestBlockChainClient::new(); - client.add_blocks(2, EachBlockWith::Uncle); - let queue = RwLock::new(VecDeque::new()); - let block = client.block(BlockId::Latest).unwrap().into_inner(); - let mut sync = ChainSync::new(SyncConfig::default(), &client); - sync.peers.insert(0, - PeerInfo { - // Messaging protocol - protocol_version: 2, - genesis: H256::zero(), - network_id: 0, - latest_hash: client.block_hash_delta_minus(1), - difficulty: None, - asking: PeerAsking::Nothing, - asking_blocks: Vec::new(), - asking_hash: None, - ask_time: Instant::now(), - last_sent_transactions: HashSet::new(), - expired: false, - confirmation: super::ForkConfirmation::Confirmed, - snapshot_number: None, - snapshot_hash: None, - asking_snapshot_data: None, - block_set: None, - }); - let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); - sync.propagate_proposed_blocks(&mut io, &[block]); - - // 1 message should be sent - assert_eq!(1, io.packets.len()); - // NEW_BLOCK_PACKET - assert_eq!(0x07, io.packets[0].packet_id); - } - - #[test] - fn propagates_transactions() { - let mut client = TestBlockChainClient::new(); - client.add_blocks(100, EachBlockWith::Uncle); - client.insert_transaction_to_queue(); - let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(1), &client); - let queue = RwLock::new(VecDeque::new()); - let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); - let peer_count = sync.propagate_new_transactions(&mut io); - // Try to propagate same transactions for the second time - let peer_count2 = sync.propagate_new_transactions(&mut io); - // Even after new block transactions should not be propagated twice - sync.chain_new_blocks(&mut io, &[], &[], &[], &[], &[], &[]); - // Try to propagate same transactions for the third time - let peer_count3 = sync.propagate_new_transactions(&mut io); - - // 1 message should be send - assert_eq!(1, io.packets.len()); - // 1 peer should be updated but only once - assert_eq!(1, peer_count); - assert_eq!(0, peer_count2); - assert_eq!(0, peer_count3); - // TRANSACTIONS_PACKET - assert_eq!(0x02, io.packets[0].packet_id); - } - - #[test] - fn does_not_propagate_new_transactions_after_new_block() { - let mut client = TestBlockChainClient::new(); - client.add_blocks(100, EachBlockWith::Uncle); - client.insert_transaction_to_queue(); - let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(1), &client); - let queue = RwLock::new(VecDeque::new()); - let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); - let peer_count = sync.propagate_new_transactions(&mut io); - io.chain.insert_transaction_to_queue(); - // New block import should not trigger propagation. - // (we only propagate on timeout) - sync.chain_new_blocks(&mut io, &[], &[], &[], &[], &[], &[]); - - // 2 message should be send - assert_eq!(1, io.packets.len()); - // 1 peer should receive the message - assert_eq!(1, peer_count); - // TRANSACTIONS_PACKET - assert_eq!(0x02, io.packets[0].packet_id); - } - - #[test] - fn does_not_fail_for_no_peers() { - let mut client = TestBlockChainClient::new(); - client.add_blocks(100, EachBlockWith::Uncle); - client.insert_transaction_to_queue(); - // Sync with no peers - let mut sync = ChainSync::new(SyncConfig::default(), &client); - let queue = RwLock::new(VecDeque::new()); - let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); - let peer_count = sync.propagate_new_transactions(&mut io); - sync.chain_new_blocks(&mut io, &[], &[], &[], &[], &[], &[]); - // Try to propagate same transactions for the second time - let peer_count2 = sync.propagate_new_transactions(&mut io); - - assert_eq!(0, io.packets.len()); - assert_eq!(0, peer_count); - assert_eq!(0, peer_count2); - } - - #[test] - fn propagates_transactions_without_alternating() { - let mut client = TestBlockChainClient::new(); - client.add_blocks(100, EachBlockWith::Uncle); - client.insert_transaction_to_queue(); - let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(1), &client); - let queue = RwLock::new(VecDeque::new()); - let ss = TestSnapshotService::new(); - // should sent some - { - let mut io = TestIo::new(&mut client, &ss, &queue, None); - let peer_count = sync.propagate_new_transactions(&mut io); - assert_eq!(1, io.packets.len()); - assert_eq!(1, peer_count); - } - // Insert some more - client.insert_transaction_to_queue(); - let (peer_count2, peer_count3) = { - let mut io = TestIo::new(&mut client, &ss, &queue, None); - // Propagate new transactions - let peer_count2 = sync.propagate_new_transactions(&mut io); - // And now the peer should have all transactions - let peer_count3 = sync.propagate_new_transactions(&mut io); - (peer_count2, peer_count3) - }; - - // 2 message should be send (in total) - assert_eq!(2, queue.read().len()); - // 1 peer should be updated but only once after inserting new transaction - assert_eq!(1, peer_count2); - assert_eq!(0, peer_count3); - // TRANSACTIONS_PACKET - assert_eq!(0x02, queue.read()[0].packet_id); - assert_eq!(0x02, queue.read()[1].packet_id); - } - - #[test] - fn should_maintain_transations_propagation_stats() { - let mut client = TestBlockChainClient::new(); - client.add_blocks(100, EachBlockWith::Uncle); - client.insert_transaction_to_queue(); - let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(1), &client); - let queue = RwLock::new(VecDeque::new()); - let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); - sync.propagate_new_transactions(&mut io); - - let stats = sync.transactions_stats(); - assert_eq!(stats.len(), 1, "Should maintain stats for single transaction.") - } - - #[test] - fn should_propagate_service_transaction_to_selected_peers_only() { - let mut client = TestBlockChainClient::new(); - client.insert_transaction_with_gas_price_to_queue(U256::zero()); - let block_hash = client.block_hash_delta_minus(1); - let mut sync = ChainSync::new(SyncConfig::default(), &client); - let queue = RwLock::new(VecDeque::new()); - let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); - - // when peer#1 is Geth - insert_dummy_peer(&mut sync, 1, block_hash); - io.peers_info.insert(1, "Geth".to_owned()); - // and peer#2 is Parity, accepting service transactions - insert_dummy_peer(&mut sync, 2, block_hash); - io.peers_info.insert(2, "Parity/v1.6".to_owned()); - // and peer#3 is Parity, discarding service transactions - insert_dummy_peer(&mut sync, 3, block_hash); - io.peers_info.insert(3, "Parity/v1.5".to_owned()); - // and peer#4 is Parity, accepting service transactions - insert_dummy_peer(&mut sync, 4, block_hash); - io.peers_info.insert(4, "Parity/v1.7.3-ABCDEFGH".to_owned()); - - // and new service transaction is propagated to peers - sync.propagate_new_transactions(&mut io); - - // peer#2 && peer#4 are receiving service transaction - assert!(io.packets.iter().any(|p| p.packet_id == 0x02 && p.recipient == 2)); // TRANSACTIONS_PACKET - assert!(io.packets.iter().any(|p| p.packet_id == 0x02 && p.recipient == 4)); // TRANSACTIONS_PACKET - assert_eq!(io.packets.len(), 2); - } - - #[test] - fn should_propagate_service_transaction_is_sent_as_separate_message() { - let mut client = TestBlockChainClient::new(); - let tx1_hash = client.insert_transaction_to_queue(); - let tx2_hash = client.insert_transaction_with_gas_price_to_queue(U256::zero()); - let block_hash = client.block_hash_delta_minus(1); - let mut sync = ChainSync::new(SyncConfig::default(), &client); - let queue = RwLock::new(VecDeque::new()); - let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); - - // when peer#1 is Parity, accepting service transactions - insert_dummy_peer(&mut sync, 1, block_hash); - io.peers_info.insert(1, "Parity/v1.6".to_owned()); - - // and service + non-service transactions are propagated to peers - sync.propagate_new_transactions(&mut io); - - // two separate packets for peer are queued: - // 1) with non-service-transaction - // 2) with service transaction - let sent_transactions: Vec = io.packets.iter() - .filter_map(|p| { - if p.packet_id != 0x02 || p.recipient != 1 { // TRANSACTIONS_PACKET - return None; - } - - let rlp = UntrustedRlp::new(&*p.data); - let item_count = rlp.item_count().unwrap_or(0); - if item_count != 1 { - return None; - } - - rlp.at(0).ok().and_then(|r| r.as_val().ok()) - }) - .collect(); - assert_eq!(sent_transactions.len(), 2); - assert!(sent_transactions.iter().any(|tx| tx.hash() == tx1_hash)); - assert!(sent_transactions.iter().any(|tx| tx.hash() == tx2_hash)); - } - - #[test] - fn handles_peer_new_block_malformed() { - let mut client = TestBlockChainClient::new(); - client.add_blocks(10, EachBlockWith::Uncle); - - let block_data = get_dummy_block(11, client.chain_info().best_block_hash); - - let queue = RwLock::new(VecDeque::new()); - let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); - //sync.have_common_block = true; - let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); - - let block = UntrustedRlp::new(&block_data); - - let result = sync.on_peer_new_block(&mut io, 0, &block); - - assert!(result.is_err()); - } - - #[test] - fn handles_peer_new_block() { - let mut client = TestBlockChainClient::new(); - client.add_blocks(10, EachBlockWith::Uncle); - - let block_data = get_dummy_blocks(11, client.chain_info().best_block_hash); - - let queue = RwLock::new(VecDeque::new()); - let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); - let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); - - let block = UntrustedRlp::new(&block_data); - - let result = sync.on_peer_new_block(&mut io, 0, &block); - - assert!(result.is_ok()); - } - - #[test] - fn handles_peer_new_block_empty() { - let mut client = TestBlockChainClient::new(); - client.add_blocks(10, EachBlockWith::Uncle); - let queue = RwLock::new(VecDeque::new()); - let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); - let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); - - let empty_data = vec![]; - let block = UntrustedRlp::new(&empty_data); - - let result = sync.on_peer_new_block(&mut io, 0, &block); - - assert!(result.is_err()); - } - - #[test] - fn handles_peer_new_hashes() { - let mut client = TestBlockChainClient::new(); - client.add_blocks(10, EachBlockWith::Uncle); - let queue = RwLock::new(VecDeque::new()); - let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); - let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); - - let hashes_data = get_dummy_hashes(); - let hashes_rlp = UntrustedRlp::new(&hashes_data); - - let result = sync.on_peer_new_hashes(&mut io, 0, &hashes_rlp); - - assert!(result.is_ok()); - } - - #[test] - fn handles_peer_new_hashes_empty() { - let mut client = TestBlockChainClient::new(); - client.add_blocks(10, EachBlockWith::Uncle); - let queue = RwLock::new(VecDeque::new()); - let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); - let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); - - let empty_hashes_data = vec![]; - let hashes_rlp = UntrustedRlp::new(&empty_hashes_data); - - let result = sync.on_peer_new_hashes(&mut io, 0, &hashes_rlp); - - assert!(result.is_ok()); - } - - // idea is that what we produce when propagading latest hashes should be accepted in - // on_peer_new_hashes in our code as well - #[test] - fn hashes_rlp_mutually_acceptable() { - let mut client = TestBlockChainClient::new(); - client.add_blocks(100, EachBlockWith::Uncle); - let queue = RwLock::new(VecDeque::new()); - let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); - let chain_info = client.chain_info(); - let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); - - let peers = sync.get_lagging_peers(&chain_info); - sync.propagate_new_hashes(&chain_info, &mut io, &peers); - - let data = &io.packets[0].data.clone(); - let result = sync.on_peer_new_hashes(&mut io, 0, &UntrustedRlp::new(data)); - assert!(result.is_ok()); - } - - // idea is that what we produce when propagading latest block should be accepted in - // on_peer_new_block in our code as well - #[test] - fn block_rlp_mutually_acceptable() { - let mut client = TestBlockChainClient::new(); - client.add_blocks(100, EachBlockWith::Uncle); - let queue = RwLock::new(VecDeque::new()); - let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); - let chain_info = client.chain_info(); - let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); - - let peers = sync.get_lagging_peers(&chain_info); - sync.propagate_blocks(&chain_info, &mut io, &[], &peers); - - let data = &io.packets[0].data.clone(); - let result = sync.on_peer_new_block(&mut io, 0, &UntrustedRlp::new(data)); - assert!(result.is_ok()); - } - - #[test] - fn should_add_transactions_to_queue() { - fn sender(tx: &UnverifiedTransaction) -> Address { - ethkey::public_to_address(&tx.recover_public().unwrap()) - } - - // given - let mut client = TestBlockChainClient::new(); - client.add_blocks(98, EachBlockWith::Uncle); - client.add_blocks(1, EachBlockWith::UncleAndTransaction); - client.add_blocks(1, EachBlockWith::Transaction); - let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); - - let good_blocks = vec![client.block_hash_delta_minus(2)]; - let retracted_blocks = vec![client.block_hash_delta_minus(1)]; - - // Add some balance to clients and reset nonces - for h in &[good_blocks[0], retracted_blocks[0]] { - let block = client.block(BlockId::Hash(*h)).unwrap(); - let sender = sender(&block.transactions()[0]);; - client.set_balance(sender, U256::from(10_000_000_000_000_000_000u64)); - client.set_nonce(sender, U256::from(0)); - } - - - // when - { - let queue = RwLock::new(VecDeque::new()); - let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); - io.chain.miner.chain_new_blocks(io.chain, &[], &[], &[], &good_blocks); - sync.chain_new_blocks(&mut io, &[], &[], &[], &good_blocks, &[], &[]); - assert_eq!(io.chain.miner.status().transactions_in_future_queue, 0); - assert_eq!(io.chain.miner.status().transactions_in_pending_queue, 1); - } - // We need to update nonce status (because we say that the block has been imported) - for h in &[good_blocks[0]] { - let block = client.block(BlockId::Hash(*h)).unwrap(); - client.set_nonce(sender(&block.transactions()[0]), U256::from(1)); - } - { - let queue = RwLock::new(VecDeque::new()); - let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&client, &ss, &queue, None); - io.chain.miner.chain_new_blocks(io.chain, &[], &[], &good_blocks, &retracted_blocks); - sync.chain_new_blocks(&mut io, &[], &[], &good_blocks, &retracted_blocks, &[], &[]); - } - - // then - let status = client.miner.status(); - assert_eq!(status.transactions_in_pending_queue, 1); - assert_eq!(status.transactions_in_future_queue, 0); - } - - #[test] - fn should_not_add_transactions_to_queue_if_not_synced() { - // given - let mut client = TestBlockChainClient::new(); - client.add_blocks(98, EachBlockWith::Uncle); - client.add_blocks(1, EachBlockWith::UncleAndTransaction); - client.add_blocks(1, EachBlockWith::Transaction); - let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); - - let good_blocks = vec![client.block_hash_delta_minus(2)]; - let retracted_blocks = vec![client.block_hash_delta_minus(1)]; - - let queue = RwLock::new(VecDeque::new()); - let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); - - // when - sync.chain_new_blocks(&mut io, &[], &[], &[], &good_blocks, &[], &[]); - assert_eq!(io.chain.miner.status().transactions_in_future_queue, 0); - assert_eq!(io.chain.miner.status().transactions_in_pending_queue, 0); - sync.chain_new_blocks(&mut io, &[], &[], &good_blocks, &retracted_blocks, &[], &[]); - - // then - let status = io.chain.miner.status(); - assert_eq!(status.transactions_in_pending_queue, 0); - assert_eq!(status.transactions_in_future_queue, 0); - } -} diff --git a/test.sh b/test.sh index 69204fef2f5fb2b208c6c782b1cd4cdb84598ef6..9e273c7492a78f9871eca80ea7392c1c71dc39ac 100755 --- a/test.sh +++ b/test.sh @@ -3,31 +3,58 @@ FEATURES="json-tests" OPTIONS="--release" +VALIDATE=1 case $1 in - --no-json) + --no-json) FEATURES="ipc" shift # past argument=value ;; - --no-release) - OPTIONS="" - shift - ;; - --no-run) - OPTIONS="--no-run" - shift - ;; - *) - # unknown option + --no-release) + OPTIONS="" + shift + ;; + --no-validate) + VALIDATE=0 + shift + ;; + --no-run) + OPTIONS="--no-run" + shift + ;; + *) + # unknown option ;; esac set -e +if [ "$VALIDATE" -eq "1" ]; then +# Validate --no-default-features build +echo "________Validate build________" +cargo check --no-default-features +cargo check --manifest-path util/io/Cargo.toml --no-default-features +cargo check --manifest-path util/io/Cargo.toml --features "mio" + # Validate chainspecs +echo "________Validate chainspecs________" ./scripts/validate_chainspecs.sh +fi -cargo test -j 8 $OPTIONS --features "$FEATURES" --all --exclude evmjit $1 -# Validate --no-default-features build -cargo check --no-default-features +# Running the C example +echo "________Running the C example________" +cd parity-clib-example && \ + mkdir -p build && \ + cd build && \ + cmake .. && \ + make && \ + ./parity-example && \ + cd .. && \ + rm -rf build && \ + cd .. + +# Running tests +echo "________Running Parity Full Test Suite________" +git submodule update --init --recursive +cargo test -j 8 $OPTIONS --features "$FEATURES" --all $1 diff --git a/transaction-pool/Cargo.toml b/transaction-pool/Cargo.toml index 49bf2db64dbb2d98e267052807cfee29e4d18747..0ba1790a47f4539268f62cb2c6e8cfac8c0dc758 100644 --- a/transaction-pool/Cargo.toml +++ b/transaction-pool/Cargo.toml @@ -1,7 +1,7 @@ [package] description = "Generic transaction pool." name = "transaction-pool" -version = "1.11.0" +version = "1.12.1" license = "GPL-3.0" authors = ["Parity Technologies "] @@ -9,4 +9,7 @@ authors = ["Parity Technologies "] error-chain = "0.11" log = "0.3" smallvec = "0.4" -ethereum-types = "0.2" +trace-time = { path = "../util/trace-time", version = "0.1" } + +[dev-dependencies] +ethereum-types = "0.3" diff --git a/transaction-pool/src/error.rs b/transaction-pool/src/error.rs index 706a5b77b19fa8d8e283ea55a2c267b0d5beb1c5..c7666841a2c9c450f834bf833ca15a9bdb65491c 100644 --- a/transaction-pool/src/error.rs +++ b/transaction-pool/src/error.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -14,24 +14,26 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use ethereum_types::H256; +/// Error chain doesn't let us have generic types. +/// So the hashes are converted to debug strings for easy display. +type Hash = String; error_chain! { errors { /// Transaction is already imported - AlreadyImported(hash: H256) { + AlreadyImported(hash: Hash) { description("transaction is already in the pool"), - display("[{:?}] transaction already imported", hash) + display("[{}] already imported", hash) } /// Transaction is too cheap to enter the queue - TooCheapToEnter(hash: H256) { + TooCheapToEnter(hash: Hash, min_score: String) { description("the pool is full and transaction is too cheap to replace any transaction"), - display("[{:?}] transaction too cheap to enter the pool", hash) + display("[{}] too cheap to enter the pool. Min score: {}", hash, min_score) } /// Transaction is too cheap to replace existing transaction that occupies the same slot. - TooCheapToReplace(old_hash: H256, hash: H256) { + TooCheapToReplace(old_hash: Hash, hash: Hash) { description("transaction is too cheap to replace existing transaction in the pool"), - display("[{:?}] transaction too cheap to replace: {:?}", hash, old_hash) + display("[{}] too cheap to replace: {}", hash, old_hash) } } } @@ -43,7 +45,7 @@ impl PartialEq for ErrorKind { match (self, other) { (&AlreadyImported(ref h1), &AlreadyImported(ref h2)) => h1 == h2, - (&TooCheapToEnter(ref h1), &TooCheapToEnter(ref h2)) => h1 == h2, + (&TooCheapToEnter(ref h1, ref s1), &TooCheapToEnter(ref h2, ref s2)) => h1 == h2 && s1 == s2, (&TooCheapToReplace(ref old1, ref new1), &TooCheapToReplace(ref old2, ref new2)) => old1 == old2 && new1 == new2, _ => false, } diff --git a/transaction-pool/src/lib.rs b/transaction-pool/src/lib.rs index 29353a1000869cd859fd715d2699f231382e256c..ea77debfa2f965d5da16c243b5f76d331e7e2be2 100644 --- a/transaction-pool/src/lib.rs +++ b/transaction-pool/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -69,13 +69,16 @@ #![warn(missing_docs)] extern crate smallvec; -extern crate ethereum_types; +extern crate trace_time; #[macro_use] extern crate error_chain; #[macro_use] extern crate log; +#[cfg(test)] +extern crate ethereum_types; + #[cfg(test)] mod tests; @@ -90,29 +93,32 @@ mod verifier; pub mod scoring; +pub use self::error::{Error, ErrorKind}; pub use self::listener::{Listener, NoopListener}; pub use self::options::Options; -pub use self::pool::{Pool, PendingIterator}; +pub use self::pool::{Pool, PendingIterator, Transaction}; pub use self::ready::{Ready, Readiness}; pub use self::scoring::Scoring; pub use self::status::{LightStatus, Status}; pub use self::verifier::Verifier; use std::fmt; - -use ethereum_types::{H256, Address}; +use std::hash::Hash; /// Already verified transaction that can be safely queued. pub trait VerifiedTransaction: fmt::Debug { + /// Transaction hash type. + type Hash: fmt::Debug + fmt::LowerHex + Eq + Clone + Hash; + + /// Transaction sender type. + type Sender: fmt::Debug + Eq + Clone + Hash; + /// Transaction hash - fn hash(&self) -> &H256; + fn hash(&self) -> &Self::Hash; /// Memory usage fn mem_usage(&self) -> usize; /// Transaction sender - fn sender(&self) -> &Address; - - /// Unique index of insertion (lower = older). - fn insertion_id(&self) -> u64; + fn sender(&self) -> &Self::Sender; } diff --git a/transaction-pool/src/listener.rs b/transaction-pool/src/listener.rs index 2fc55528fe818126d6d86ac3e226d4cd7d628ce8..3339a7730d1d3ac2c2192fedb8ab83a5b9672815 100644 --- a/transaction-pool/src/listener.rs +++ b/transaction-pool/src/listener.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -15,6 +15,7 @@ // along with Parity. If not, see . use std::sync::Arc; +use error::ErrorKind; /// Transaction pool listener. /// @@ -28,16 +29,16 @@ pub trait Listener { /// The transaction was rejected from the pool. /// It means that it was too cheap to replace any transaction already in the pool. - fn rejected(&mut self, _tx: T) {} + fn rejected(&mut self, _tx: &Arc, _reason: &ErrorKind) {} - /// The transaction was dropped from the pool because of a limit. - fn dropped(&mut self, _tx: &Arc) {} + /// The transaction was pushed out from the pool because of the limit. + fn dropped(&mut self, _tx: &Arc, _by: Option<&T>) {} /// The transaction was marked as invalid by executor. fn invalid(&mut self, _tx: &Arc) {} - /// The transaction has been cancelled. - fn cancelled(&mut self, _tx: &Arc) {} + /// The transaction has been canceled. + fn canceled(&mut self, _tx: &Arc) {} /// The transaction has been mined. fn mined(&mut self, _tx: &Arc) {} @@ -47,3 +48,38 @@ pub trait Listener { #[derive(Debug)] pub struct NoopListener; impl Listener for NoopListener {} + +impl Listener for (A, B) where + A: Listener, + B: Listener, +{ + fn added(&mut self, tx: &Arc, old: Option<&Arc>) { + self.0.added(tx, old); + self.1.added(tx, old); + } + + fn rejected(&mut self, tx: &Arc, reason: &ErrorKind) { + self.0.rejected(tx, reason); + self.1.rejected(tx, reason); + } + + fn dropped(&mut self, tx: &Arc, by: Option<&T>) { + self.0.dropped(tx, by); + self.1.dropped(tx, by); + } + + fn invalid(&mut self, tx: &Arc) { + self.0.invalid(tx); + self.1.invalid(tx); + } + + fn canceled(&mut self, tx: &Arc) { + self.0.canceled(tx); + self.1.canceled(tx); + } + + fn mined(&mut self, tx: &Arc) { + self.0.mined(tx); + self.1.mined(tx); + } +} diff --git a/transaction-pool/src/options.rs b/transaction-pool/src/options.rs index ddec912864fe2d644e38b9846111623545d5847b..291001a20270434c7c4f335d19e9e983c537df66 100644 --- a/transaction-pool/src/options.rs +++ b/transaction-pool/src/options.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -15,7 +15,7 @@ // along with Parity. If not, see . /// Transaction Pool options. -#[derive(Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct Options { /// Maximal number of transactions in the pool. pub max_count: usize, diff --git a/transaction-pool/src/pool.rs b/transaction-pool/src/pool.rs index 4f4a63920f1cf68e27b381d93322c9f7e88b8b07..4bbf00ef24067cb97250a76e2272beee01e2be35 100644 --- a/transaction-pool/src/pool.rs +++ b/transaction-pool/src/pool.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -15,9 +15,8 @@ // along with Parity. If not, see . use std::sync::Arc; -use std::collections::{HashMap, BTreeSet}; - -use ethereum_types::{H160, H256}; +use std::slice; +use std::collections::{hash_map, HashMap, BTreeSet}; use error; use listener::{Listener, NoopListener}; @@ -29,21 +28,51 @@ use transactions::{AddResult, Transactions}; use {VerifiedTransaction}; -type Sender = H160; +/// Internal representation of transaction. +/// +/// Includes unique insertion id that can be used for scoring explictly, +/// but internally is used to resolve conflicts in case of equal scoring +/// (newer transactionsa are preferred). +#[derive(Debug)] +pub struct Transaction { + /// Sequential id of the transaction + pub insertion_id: u64, + /// Shared transaction + pub transaction: Arc, +} + +impl Clone for Transaction { + fn clone(&self) -> Self { + Transaction { + insertion_id: self.insertion_id, + transaction: self.transaction.clone(), + } + } +} + +impl ::std::ops::Deref for Transaction { + type Target = Arc; + + fn deref(&self) -> &Self::Target { + &self.transaction + } +} /// A transaction pool. #[derive(Debug)] -pub struct Pool, L = NoopListener> { +pub struct Pool, L = NoopListener> { listener: L, scoring: S, options: Options, mem_usage: usize, - transactions: HashMap>, - by_hash: HashMap>, + transactions: HashMap>, + by_hash: HashMap>, best_transactions: BTreeSet>, worst_transactions: BTreeSet>, + + insertion_id: u64, } impl + Default> Default for Pool { @@ -67,7 +96,6 @@ impl> Pool { } } - const INITIAL_NUMBER_OF_SENDERS: usize = 16; impl Pool where @@ -89,6 +117,7 @@ impl Pool where by_hash, best_transactions: Default::default(), worst_transactions: Default::default(), + insertion_id: 0, } } @@ -104,22 +133,28 @@ impl Pool where /// If any limit is reached the transaction with the lowest `Score` is evicted to make room. /// /// The `Listener` will be informed on any drops or rejections. - pub fn import(&mut self, mut transaction: T) -> error::Result> { + pub fn import(&mut self, transaction: T) -> error::Result> { let mem_usage = transaction.mem_usage(); - ensure!(!self.by_hash.contains_key(transaction.hash()), error::ErrorKind::AlreadyImported(*transaction.hash())); + ensure!(!self.by_hash.contains_key(transaction.hash()), error::ErrorKind::AlreadyImported(format!("{:?}", transaction.hash()))); - // TODO [ToDr] Most likely move this after the transsaction is inserted. + self.insertion_id += 1; + let mut transaction = Transaction { + insertion_id: self.insertion_id, + transaction: Arc::new(transaction), + }; + + // TODO [ToDr] Most likely move this after the transaction is inserted. // Avoid using should_replace, but rather use scoring for that. { let remove_worst = |s: &mut Self, transaction| { match s.remove_worst(&transaction) { Err(err) => { - s.listener.rejected(transaction); + s.listener.rejected(&transaction, err.kind()); Err(err) }, Ok(removed) => { - s.listener.dropped(&removed); + s.listener.dropped(&removed, Some(&transaction)); s.finalize_remove(removed.hash()); Ok(transaction) }, @@ -127,16 +162,18 @@ impl Pool where }; while self.by_hash.len() + 1 > self.options.max_count { + trace!("Count limit reached: {} > {}", self.by_hash.len() + 1, self.options.max_count); transaction = remove_worst(self, transaction)?; } while self.mem_usage + mem_usage > self.options.max_mem_usage { + trace!("Mem limit reached: {} > {}", self.mem_usage + mem_usage, self.options.max_mem_usage); transaction = remove_worst(self, transaction)?; } } let (result, prev_state, current_state) = { - let transactions = self.transactions.entry(*transaction.sender()).or_insert_with(Transactions::default); + let transactions = self.transactions.entry(transaction.sender().clone()).or_insert_with(Transactions::default); // get worst and best transactions for comparison let prev = transactions.worst_and_best(); let result = transactions.add(transaction, &self.scoring, self.options.max_per_sender); @@ -151,31 +188,31 @@ impl Pool where AddResult::Ok(tx) => { self.listener.added(&tx, None); self.finalize_insert(&tx, None); - Ok(tx) + Ok(tx.transaction) }, AddResult::PushedOut { new, old } | AddResult::Replaced { new, old } => { self.listener.added(&new, Some(&old)); self.finalize_insert(&new, Some(&old)); - Ok(new) + Ok(new.transaction) }, AddResult::TooCheap { new, old } => { - let hash = *new.hash(); - self.listener.rejected(new); - bail!(error::ErrorKind::TooCheapToReplace(*old.hash(), hash)) + let error = error::ErrorKind::TooCheapToReplace(format!("{:x}", old.hash()), format!("{:x}", new.hash())); + self.listener.rejected(&new, &error); + bail!(error) }, - AddResult::TooCheapToEnter(new) => { - let hash = *new.hash(); - self.listener.rejected(new); - bail!(error::ErrorKind::TooCheapToEnter(hash)) + AddResult::TooCheapToEnter(new, score) => { + let error = error::ErrorKind::TooCheapToEnter(format!("{:x}", new.hash()), format!("{:?}", score)); + self.listener.rejected(&new, &error); + bail!(error) } } } /// Updates state of the pool statistics if the transaction was added to a set. - fn finalize_insert(&mut self, new: &Arc, old: Option<&Arc>) { + fn finalize_insert(&mut self, new: &Transaction, old: Option<&Transaction>) { self.mem_usage += new.mem_usage(); - self.by_hash.insert(*new.hash(), new.clone()); + self.by_hash.insert(new.hash().clone(), new.clone()); if let Some(old) = old { self.finalize_remove(old.hash()); @@ -183,23 +220,23 @@ impl Pool where } /// Updates the pool statistics if transaction was removed. - fn finalize_remove(&mut self, hash: &H256) -> Option> { + fn finalize_remove(&mut self, hash: &T::Hash) -> Option> { self.by_hash.remove(hash).map(|old| { - self.mem_usage -= old.mem_usage(); - old + self.mem_usage -= old.transaction.mem_usage(); + old.transaction }) } /// Updates best and worst transactions from a sender. fn update_senders_worst_and_best( &mut self, - previous: Option<((S::Score, Arc), (S::Score, Arc))>, - current: Option<((S::Score, Arc), (S::Score, Arc))>, + previous: Option<((S::Score, Transaction), (S::Score, Transaction))>, + current: Option<((S::Score, Transaction), (S::Score, Transaction))>, ) { let worst_collection = &mut self.worst_transactions; let best_collection = &mut self.best_transactions; - let is_same = |a: &(S::Score, Arc), b: &(S::Score, Arc)| { + let is_same = |a: &(S::Score, Transaction), b: &(S::Score, Transaction)| { a.0 == b.0 && a.1.hash() == b.1.hash() }; @@ -236,19 +273,19 @@ impl Pool where } /// Attempts to remove the worst transaction from the pool if it's worse than the given one. - fn remove_worst(&mut self, transaction: &T) -> error::Result> { + fn remove_worst(&mut self, transaction: &Transaction) -> error::Result> { let to_remove = match self.worst_transactions.iter().next_back() { // No elements to remove? and the pool is still full? None => { warn!("The pool is full but there are no transactions to remove."); - return Err(error::ErrorKind::TooCheapToEnter(*transaction.hash()).into()); + return Err(error::ErrorKind::TooCheapToEnter(format!("{:?}", transaction.hash()), "unknown".into()).into()); }, Some(old) => if self.scoring.should_replace(&old.transaction, transaction) { // New transaction is better than the worst one so we can replace it. old.clone() } else { // otherwise fail - return Err(error::ErrorKind::TooCheapToEnter(*transaction.hash()).into()) + return Err(error::ErrorKind::TooCheapToEnter(format!("{:?}", transaction.hash()), format!("{:?}", old.score)).into()) }, }; @@ -256,11 +293,12 @@ impl Pool where self.remove_from_set(to_remove.transaction.sender(), |set, scoring| { set.remove(&to_remove.transaction, scoring) }); + Ok(to_remove.transaction) } /// Removes transaction from sender's transaction `HashMap`. - fn remove_from_set, &S) -> R>(&mut self, sender: &Sender, f: F) -> Option { + fn remove_from_set, &S) -> R>(&mut self, sender: &T::Sender, f: F) -> Option { let (prev, next, result) = if let Some(set) = self.transactions.get_mut(sender) { let prev = set.worst_and_best(); let result = f(set, &self.scoring); @@ -283,14 +321,14 @@ impl Pool where self.worst_transactions.clear(); for (_hash, tx) in self.by_hash.drain() { - self.listener.dropped(&tx) + self.listener.dropped(&tx.transaction, None) } } /// Removes single transaction from the pool. /// Depending on the `is_invalid` flag the listener /// will either get a `cancelled` or `invalid` notification. - pub fn remove(&mut self, hash: &H256, is_invalid: bool) -> Option> { + pub fn remove(&mut self, hash: &T::Hash, is_invalid: bool) -> Option> { if let Some(tx) = self.finalize_remove(hash) { self.remove_from_set(tx.sender(), |set, scoring| { set.remove(&tx, scoring) @@ -298,7 +336,7 @@ impl Pool where if is_invalid { self.listener.invalid(&tx); } else { - self.listener.cancelled(&tx); + self.listener.canceled(&tx); } Some(tx) } else { @@ -307,7 +345,7 @@ impl Pool where } /// Removes all stalled transactions from given sender. - fn remove_stalled>(&mut self, sender: &Sender, ready: &mut R) -> usize { + fn remove_stalled>(&mut self, sender: &T::Sender, ready: &mut R) -> usize { let removed_from_set = self.remove_from_set(sender, |transactions, scoring| { transactions.cull(ready, scoring) }); @@ -326,7 +364,7 @@ impl Pool where } /// Removes all stalled transactions from given sender list (or from all senders). - pub fn cull>(&mut self, senders: Option<&[Sender]>, mut ready: R) -> usize { + pub fn cull>(&mut self, senders: Option<&[T::Sender]>, mut ready: R) -> usize { let mut removed = 0; match senders { Some(senders) => { @@ -345,6 +383,16 @@ impl Pool where removed } + /// Returns a transaction if it's part of the pool or `None` otherwise. + pub fn find(&self, hash: &T::Hash) -> Option> { + self.by_hash.get(hash).map(|t| t.transaction.clone()) + } + + /// Returns worst transaction in the queue (if any). + pub fn worst_transaction(&self) -> Option> { + self.worst_transactions.iter().next().map(|x| x.transaction.transaction.clone()) + } + /// Returns an iterator of pending (ready) transactions. pub fn pending>(&self, ready: R) -> PendingIterator { PendingIterator { @@ -354,6 +402,50 @@ impl Pool where } } + /// Returns pending (ready) transactions from given sender. + pub fn pending_from_sender>(&self, ready: R, sender: &T::Sender) -> PendingIterator { + let best_transactions = self.transactions.get(sender) + .and_then(|transactions| transactions.worst_and_best()) + .map(|(_, best)| ScoreWithRef::new(best.0, best.1)) + .map(|s| { + let mut set = BTreeSet::new(); + set.insert(s); + set + }) + .unwrap_or_default(); + + PendingIterator { + ready, + best_transactions, + pool: self, + } + } + + /// Returns unprioritized list of ready transactions. + pub fn unordered_pending>(&self, ready: R) -> UnorderedIterator { + UnorderedIterator { + ready, + senders: self.transactions.iter(), + transactions: None, + } + } + + /// Update score of transactions of a particular sender. + pub fn update_scores(&mut self, sender: &T::Sender, event: S::Event) { + let res = if let Some(set) = self.transactions.get_mut(sender) { + let prev = set.worst_and_best(); + set.update_scores(&self.scoring, event); + let current = set.worst_and_best(); + Some((prev, current)) + } else { + None + }; + + if let Some((prev, current)) = res { + self.update_senders_worst_and_best(prev, current); + } + } + /// Computes the full status of the pool (including readiness). pub fn status>(&self, mut ready: R) -> Status { let mut status = Status::default(); @@ -362,7 +454,7 @@ impl Pool where let len = transactions.len(); for (idx, tx) in transactions.iter().enumerate() { match ready.is_ready(tx) { - Readiness::Stalled => status.stalled += 1, + Readiness::Stale => status.stalled += 1, Readiness::Ready => status.pending += 1, Readiness::Future => { status.future += len - idx; @@ -383,8 +475,67 @@ impl Pool where senders: self.transactions.len(), } } + + /// Returns current pool options. + pub fn options(&self) -> Options { + self.options.clone() + } + + /// Borrows listener instance. + pub fn listener(&self) -> &L { + &self.listener + } + + /// Borrows listener mutably. + pub fn listener_mut(&mut self) -> &mut L { + &mut self.listener + } +} + +/// An iterator over all pending (ready) transactions in unoredered fashion. +/// +/// NOTE: Current implementation will iterate over all transactions from particular sender +/// ordered by nonce, but that might change in the future. +/// +/// NOTE: the transactions are not removed from the queue. +/// You might remove them later by calling `cull`. +pub struct UnorderedIterator<'a, T, R, S> where + T: VerifiedTransaction + 'a, + S: Scoring + 'a, +{ + ready: R, + senders: hash_map::Iter<'a, T::Sender, Transactions>, + transactions: Option>>, +} + +impl<'a, T, R, S> Iterator for UnorderedIterator<'a, T, R, S> where + T: VerifiedTransaction, + R: Ready, + S: Scoring, +{ + type Item = Arc; + + fn next(&mut self) -> Option { + loop { + if let Some(transactions) = self.transactions.as_mut() { + if let Some(tx) = transactions.next() { + match self.ready.is_ready(&tx) { + Readiness::Ready => { + return Some(tx.transaction.clone()); + }, + state => trace!("[{:?}] Ignoring {:?} transaction.", tx.hash(), state), + } + } + } + + // otherwise fallback and try next sender + let next_sender = self.senders.next()?; + self.transactions = Some(next_sender.1.iter()); + } + } } + /// An iterator over all pending (ready) transactions. /// NOTE: the transactions are not removed from the queue. /// You might remove them later by calling `cull`. @@ -422,9 +573,9 @@ impl<'a, T, R, S, L> Iterator for PendingIterator<'a, T, R, S, L> where self.best_transactions.insert(ScoreWithRef::new(score, tx)); } - return Some(best.transaction) + return Some(best.transaction.transaction) }, - state => warn!("[{:?}] Ignoring {:?} transaction.", best.transaction.hash(), state), + state => trace!("[{:?}] Ignoring {:?} transaction.", best.transaction.hash(), state), } } diff --git a/transaction-pool/src/ready.rs b/transaction-pool/src/ready.rs index 735244432275a6894b8b8f93bca02e02e8665f48..0bee5188df285fb09392bb49215d6a33115197aa 100644 --- a/transaction-pool/src/ready.rs +++ b/transaction-pool/src/ready.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -17,8 +17,8 @@ /// Transaction readiness. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Readiness { - /// The transaction is stalled (and should/will be removed from the pool). - Stalled, + /// The transaction is stale (and should/will be removed from the pool). + Stale, /// The transaction is ready to be included in pending set. Ready, /// The transaction is not yet ready. diff --git a/transaction-pool/src/scoring.rs b/transaction-pool/src/scoring.rs index e4f923c9b5a9471853adc7bf4009d239c39bf803..462b7086519db1ae02f9a39adcafd03d1c46b52c 100644 --- a/transaction-pool/src/scoring.rs +++ b/transaction-pool/src/scoring.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -17,9 +17,7 @@ //! A transactions ordering abstraction. use std::{cmp, fmt}; -use std::sync::Arc; - -use {VerifiedTransaction}; +use pool::Transaction; /// Represents a decision what to do with /// a new transaction that tries to enter the pool. @@ -42,7 +40,7 @@ pub enum Choice { /// The `Scoring` implementations can use this information /// to update the `Score` table more efficiently. #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum Change { +pub enum Change { /// New transaction has been inserted at given index. /// The Score at that index is initialized with default value /// and needs to be filled in. @@ -56,8 +54,12 @@ pub enum Change { /// The score at that index needs to be update (it contains value from previous transaction). ReplacedAt(usize), /// Given number of stalled transactions has been culled from the beginning. - /// Usually the score will have to be re-computed from scratch. + /// The scores has been removed from the beginning as well. + /// For simple scoring algorithms no action is required here. Culled(usize), + /// Custom event to update the score triggered outside of the pool. + /// Handling this event is up to scoring implementation. + Event(T), } /// A transaction ordering. @@ -69,7 +71,7 @@ pub enum Change { /// Implementation notes: /// - Returned `Score`s should match ordering of `compare` method. /// - `compare` will be called only within a context of transactions from the same sender. -/// - `choose` will be called only if `compare` returns `Ordering::Equal` +/// - `choose` may be called even if `compare` returns `Ordering::Equal` /// - `should_replace` is used to decide if new transaction should push out an old transaction already in the queue. /// - `Score`s and `compare` should align with `Ready` implementation. /// @@ -79,9 +81,11 @@ pub enum Change { /// - `update_scores`: score defined as `gasPrice` if `n==0` and `max(scores[n-1], gasPrice)` if `n>0` /// - `should_replace`: compares `gasPrice` (decides if transaction from a different sender is more valuable) /// -pub trait Scoring { +pub trait Scoring: fmt::Debug { /// A score of a transaction. type Score: cmp::Ord + Clone + Default + fmt::Debug; + /// Custom scoring update event type. + type Event: fmt::Debug; /// Decides on ordering of `T`s from a particular sender. fn compare(&self, old: &T, other: &T) -> cmp::Ordering; @@ -92,7 +96,7 @@ pub trait Scoring { /// Updates the transaction scores given a list of transactions and a change to previous scoring. /// NOTE: you can safely assume that both slices have the same length. /// (i.e. score at index `i` represents transaction at the same index) - fn update_scores(&self, txs: &[Arc], scores: &mut [Self::Score], change: Change); + fn update_scores(&self, txs: &[Transaction], scores: &mut [Self::Score], change: Change); /// Decides if `new` should push out `old` transaction from the pool. fn should_replace(&self, old: &T, new: &T) -> bool; @@ -104,7 +108,14 @@ pub struct ScoreWithRef { /// Score pub score: S, /// Shared transaction - pub transaction: Arc, + pub transaction: Transaction, +} + +impl ScoreWithRef { + /// Creates a new `ScoreWithRef` + pub fn new(score: S, transaction: Transaction) -> Self { + ScoreWithRef { score, transaction } + } } impl Clone for ScoreWithRef { @@ -116,30 +127,23 @@ impl Clone for ScoreWithRef { } } -impl ScoreWithRef { - /// Creates a new `ScoreWithRef` - pub fn new(score: S, transaction: Arc) -> Self { - ScoreWithRef { score, transaction } - } -} - -impl Ord for ScoreWithRef { +impl Ord for ScoreWithRef { fn cmp(&self, other: &Self) -> cmp::Ordering { other.score.cmp(&self.score) - .then(other.transaction.insertion_id().cmp(&self.transaction.insertion_id())) + .then(other.transaction.insertion_id.cmp(&self.transaction.insertion_id)) } } -impl PartialOrd for ScoreWithRef { +impl PartialOrd for ScoreWithRef { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } -impl PartialEq for ScoreWithRef { +impl PartialEq for ScoreWithRef { fn eq(&self, other: &Self) -> bool { - self.score == other.score && self.transaction.insertion_id() == other.transaction.insertion_id() + self.score == other.score && self.transaction.insertion_id == other.transaction.insertion_id } } -impl Eq for ScoreWithRef {} +impl Eq for ScoreWithRef {} diff --git a/transaction-pool/src/status.rs b/transaction-pool/src/status.rs index 5862f75a1ebf393ad1d9d1059493611c1ea66671..b9e7656d445a7e7e951e9f4d019dafdfe06c00c6 100644 --- a/transaction-pool/src/status.rs +++ b/transaction-pool/src/status.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -16,7 +16,7 @@ /// Light pool status. /// This status is cheap to compute and can be called frequently. -#[derive(Default, Debug, PartialEq, Eq)] +#[derive(Default, Debug, Clone, PartialEq, Eq)] pub struct LightStatus { /// Memory usage in bytes. pub mem_usage: usize, @@ -29,7 +29,7 @@ pub struct LightStatus { /// A full queue status. /// To compute this status it is required to provide `Ready`. /// NOTE: To compute the status we need to visit each transaction in the pool. -#[derive(Default, Debug, PartialEq, Eq)] +#[derive(Default, Debug, Clone, PartialEq, Eq)] pub struct Status { /// Number of stalled transactions. pub stalled: usize, diff --git a/transaction-pool/src/tests/helpers.rs b/transaction-pool/src/tests/helpers.rs index 4e03cc89612a88e6924c16dc053028a029fbaf10..b71959b08e910dbcb1c61d33cfac3b53f7bdeaac 100644 --- a/transaction-pool/src/tests/helpers.rs +++ b/transaction-pool/src/tests/helpers.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -17,15 +17,16 @@ use std::cmp; use std::collections::HashMap; -use ethereum_types::U256; -use {scoring, Scoring, Ready, Readiness, Address as Sender}; -use super::{Transaction, SharedTransaction}; +use ethereum_types::{H160 as Sender, U256}; +use {pool, scoring, Scoring, Ready, Readiness}; +use super::Transaction; -#[derive(Default)] +#[derive(Debug, Default)] pub struct DummyScoring; impl Scoring for DummyScoring { type Score = U256; + type Event = (); fn compare(&self, old: &Transaction, new: &Transaction) -> cmp::Ordering { old.nonce.cmp(&new.nonce) @@ -43,9 +44,17 @@ impl Scoring for DummyScoring { } } - fn update_scores(&self, txs: &[SharedTransaction], scores: &mut [Self::Score], _change: scoring::Change) { - for i in 0..txs.len() { - scores[i] = txs[i].gas_price; + fn update_scores(&self, txs: &[pool::Transaction], scores: &mut [Self::Score], change: scoring::Change) { + if let scoring::Change::Event(_) = change { + // In case of event reset all scores to 0 + for i in 0..txs.len() { + scores[i] = 0.into(); + } + } else { + // Set to a gas price otherwise + for i in 0..txs.len() { + scores[i] = txs[i].gas_price; + } } } @@ -75,7 +84,7 @@ impl Ready for NonceReady { *nonce = *nonce + 1.into(); Readiness::Ready }, - cmp::Ordering::Less => Readiness::Stalled, + cmp::Ordering::Less => Readiness::Stale, } } } diff --git a/transaction-pool/src/tests/mod.rs b/transaction-pool/src/tests/mod.rs index 05b72284b5a8392bf971885b89b6be50a5982a47..77c25287570a45aaf4a8ed435a59912fb2bb5be1 100644 --- a/transaction-pool/src/tests/mod.rs +++ b/transaction-pool/src/tests/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -32,15 +32,16 @@ pub struct Transaction { pub gas_price: U256, pub gas: U256, pub sender: Address, - pub insertion_id: u64, pub mem_usage: usize, } impl VerifiedTransaction for Transaction { + type Hash = H256; + type Sender = Address; + fn hash(&self) -> &H256 { &self.hash } fn mem_usage(&self) -> usize { self.mem_usage } fn sender(&self) -> &Address { &self.sender } - fn insertion_id(&self) -> u64 { self.insertion_id } } pub type SharedTransaction = Arc; @@ -123,9 +124,9 @@ fn should_reject_if_above_count() { // Reject second let tx1 = b.tx().nonce(0).new(); let tx2 = b.tx().nonce(1).new(); - let hash = *tx2.hash(); + let hash = format!("{:?}", tx2.hash()); txq.import(tx1).unwrap(); - assert_eq!(txq.import(tx2).unwrap_err().kind(), &error::ErrorKind::TooCheapToEnter(hash)); + assert_eq!(txq.import(tx2).unwrap_err().kind(), &error::ErrorKind::TooCheapToEnter(hash, "0x0".into())); assert_eq!(txq.light_status().transaction_count, 1); txq.clear(); @@ -149,9 +150,9 @@ fn should_reject_if_above_mem_usage() { // Reject second let tx1 = b.tx().nonce(1).mem_usage(1).new(); let tx2 = b.tx().nonce(2).mem_usage(2).new(); - let hash = *tx2.hash(); + let hash = format!("{:?}", tx2.hash()); txq.import(tx1).unwrap(); - assert_eq!(txq.import(tx2).unwrap_err().kind(), &error::ErrorKind::TooCheapToEnter(hash)); + assert_eq!(txq.import(tx2).unwrap_err().kind(), &error::ErrorKind::TooCheapToEnter(hash, "0x0".into())); assert_eq!(txq.light_status().transaction_count, 1); txq.clear(); @@ -175,9 +176,9 @@ fn should_reject_if_above_sender_count() { // Reject second let tx1 = b.tx().nonce(1).new(); let tx2 = b.tx().nonce(2).new(); - let hash = *tx2.hash(); + let hash = format!("{:x}", tx2.hash()); txq.import(tx1).unwrap(); - assert_eq!(txq.import(tx2).unwrap_err().kind(), &error::ErrorKind::TooCheapToEnter(hash)); + assert_eq!(txq.import(tx2).unwrap_err().kind(), &error::ErrorKind::TooCheapToEnter(hash, "0x0".into())); assert_eq!(txq.light_status().transaction_count, 1); txq.clear(); @@ -185,10 +186,10 @@ fn should_reject_if_above_sender_count() { // Replace first let tx1 = b.tx().nonce(1).new(); let tx2 = b.tx().nonce(2).gas_price(2).new(); - let hash = *tx2.hash(); + let hash = format!("{:x}", tx2.hash()); txq.import(tx1).unwrap(); // This results in error because we also compare nonces - assert_eq!(txq.import(tx2).unwrap_err().kind(), &error::ErrorKind::TooCheapToEnter(hash)); + assert_eq!(txq.import(tx2).unwrap_err().kind(), &error::ErrorKind::TooCheapToEnter(hash, "0x0".into())); assert_eq!(txq.light_status().transaction_count, 1); } @@ -249,6 +250,126 @@ fn should_construct_pending() { assert_eq!(pending.next(), None); } +#[test] +fn should_return_unordered_iterator() { + // given + let b = TransactionBuilder::default(); + let mut txq = TestPool::default(); + + let tx0 = txq.import(b.tx().nonce(0).gas_price(5).new()).unwrap(); + let tx1 = txq.import(b.tx().nonce(1).gas_price(5).new()).unwrap(); + let tx2 = txq.import(b.tx().nonce(2).new()).unwrap(); + let tx3 = txq.import(b.tx().nonce(3).gas_price(4).new()).unwrap(); + //gap + txq.import(b.tx().nonce(5).new()).unwrap(); + + let tx5 = txq.import(b.tx().sender(1).nonce(0).new()).unwrap(); + let tx6 = txq.import(b.tx().sender(1).nonce(1).new()).unwrap(); + let tx7 = txq.import(b.tx().sender(1).nonce(2).new()).unwrap(); + let tx8 = txq.import(b.tx().sender(1).nonce(3).gas_price(4).new()).unwrap(); + // gap + txq.import(b.tx().sender(1).nonce(5).new()).unwrap(); + + let tx9 = txq.import(b.tx().sender(2).nonce(0).new()).unwrap(); + assert_eq!(txq.light_status().transaction_count, 11); + assert_eq!(txq.status(NonceReady::default()), Status { + stalled: 0, + pending: 9, + future: 2, + }); + assert_eq!(txq.status(NonceReady::new(1)), Status { + stalled: 3, + pending: 6, + future: 2, + }); + + // when + let all: Vec<_> = txq.unordered_pending(NonceReady::default()).collect(); + + let chain1 = vec![tx0, tx1, tx2, tx3]; + let chain2 = vec![tx5, tx6, tx7, tx8]; + let chain3 = vec![tx9]; + + assert_eq!(all.len(), chain1.len() + chain2.len() + chain3.len()); + + let mut options = vec![ + vec![chain1.clone(), chain2.clone(), chain3.clone()], + vec![chain2.clone(), chain1.clone(), chain3.clone()], + vec![chain2.clone(), chain3.clone(), chain1.clone()], + vec![chain3.clone(), chain2.clone(), chain1.clone()], + vec![chain3.clone(), chain1.clone(), chain2.clone()], + vec![chain1.clone(), chain3.clone(), chain2.clone()], + ].into_iter().map(|mut v| { + let mut first = v.pop().unwrap(); + for mut x in v { + first.append(&mut x); + } + first + }); + + assert!(options.any(|opt| all == opt)); +} + +#[test] +fn should_update_scoring_correctly() { + // given + let b = TransactionBuilder::default(); + let mut txq = TestPool::default(); + + let tx0 = txq.import(b.tx().nonce(0).gas_price(5).new()).unwrap(); + let tx1 = txq.import(b.tx().nonce(1).gas_price(5).new()).unwrap(); + let tx2 = txq.import(b.tx().nonce(2).new()).unwrap(); + // this transaction doesn't get to the block despite high gas price + // because of block gas limit and simplistic ordering algorithm. + txq.import(b.tx().nonce(3).gas_price(4).new()).unwrap(); + //gap + txq.import(b.tx().nonce(5).new()).unwrap(); + + let tx5 = txq.import(b.tx().sender(1).nonce(0).new()).unwrap(); + let tx6 = txq.import(b.tx().sender(1).nonce(1).new()).unwrap(); + let tx7 = txq.import(b.tx().sender(1).nonce(2).new()).unwrap(); + let tx8 = txq.import(b.tx().sender(1).nonce(3).gas_price(4).new()).unwrap(); + // gap + txq.import(b.tx().sender(1).nonce(5).new()).unwrap(); + + let tx9 = txq.import(b.tx().sender(2).nonce(0).new()).unwrap(); + assert_eq!(txq.light_status().transaction_count, 11); + assert_eq!(txq.status(NonceReady::default()), Status { + stalled: 0, + pending: 9, + future: 2, + }); + assert_eq!(txq.status(NonceReady::new(1)), Status { + stalled: 3, + pending: 6, + future: 2, + }); + + txq.update_scores(&0.into(), ()); + + // when + let mut current_gas = U256::zero(); + let limit = (21_000 * 8).into(); + let mut pending = txq.pending(NonceReady::default()).take_while(|tx| { + let should_take = tx.gas + current_gas <= limit; + if should_take { + current_gas = current_gas + tx.gas + } + should_take + }); + + assert_eq!(pending.next(), Some(tx9)); + assert_eq!(pending.next(), Some(tx5)); + assert_eq!(pending.next(), Some(tx6)); + assert_eq!(pending.next(), Some(tx7)); + assert_eq!(pending.next(), Some(tx8)); + // penalized transactions + assert_eq!(pending.next(), Some(tx0)); + assert_eq!(pending.next(), Some(tx1)); + assert_eq!(pending.next(), Some(tx2)); + assert_eq!(pending.next(), None); +} + #[test] fn should_remove_transaction() { // given @@ -375,6 +496,20 @@ fn should_re_insert_after_cull() { }); } +#[test] +fn should_return_worst_transaction() { + // given + let b = TransactionBuilder::default(); + let mut txq = TestPool::default(); + assert!(txq.worst_transaction().is_none()); + + // when + txq.import(b.tx().nonce(0).gas_price(5).new()).unwrap(); + + // then + assert!(txq.worst_transaction().is_some()); +} + mod listener { use std::cell::RefCell; use std::rc::Rc; @@ -389,11 +524,11 @@ mod listener { self.0.borrow_mut().push(if old.is_some() { "replaced" } else { "added" }); } - fn rejected(&mut self, _tx: Transaction) { + fn rejected(&mut self, _tx: &SharedTransaction, _reason: &error::ErrorKind) { self.0.borrow_mut().push("rejected".into()); } - fn dropped(&mut self, _tx: &SharedTransaction) { + fn dropped(&mut self, _tx: &SharedTransaction, _new: Option<&Transaction>) { self.0.borrow_mut().push("dropped".into()); } @@ -401,8 +536,8 @@ mod listener { self.0.borrow_mut().push("invalid".into()); } - fn cancelled(&mut self, _tx: &SharedTransaction) { - self.0.borrow_mut().push("cancelled".into()); + fn canceled(&mut self, _tx: &SharedTransaction) { + self.0.borrow_mut().push("canceled".into()); } fn mined(&mut self, _tx: &SharedTransaction) { @@ -461,9 +596,9 @@ mod listener { // then txq.remove(&tx1.hash(), false); - assert_eq!(*results.borrow(), &["added", "added", "cancelled"]); + assert_eq!(*results.borrow(), &["added", "added", "canceled"]); txq.remove(&tx2.hash(), true); - assert_eq!(*results.borrow(), &["added", "added", "cancelled", "invalid"]); + assert_eq!(*results.borrow(), &["added", "added", "canceled", "invalid"]); assert_eq!(txq.light_status().transaction_count, 0); } @@ -503,4 +638,3 @@ mod listener { assert_eq!(*results.borrow(), &["added", "added", "mined", "mined"]); } } - diff --git a/transaction-pool/src/tests/tx_builder.rs b/transaction-pool/src/tests/tx_builder.rs index e9c1c1d5fcd2d9be40234ef6e5515cfe26f613a0..9478d417a2eb268e29ee3b84d0b17d81e311b9f9 100644 --- a/transaction-pool/src/tests/tx_builder.rs +++ b/transaction-pool/src/tests/tx_builder.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -14,9 +14,6 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use std::rc::Rc; -use std::cell::Cell; - use super::{Transaction, U256, Address}; #[derive(Debug, Default, Clone)] @@ -26,7 +23,6 @@ pub struct TransactionBuilder { gas: U256, sender: Address, mem_usage: usize, - insertion_id: Rc>, } impl TransactionBuilder { @@ -55,11 +51,6 @@ impl TransactionBuilder { } pub fn new(self) -> Transaction { - let insertion_id = { - let id = self.insertion_id.get() + 1; - self.insertion_id.set(id); - id - }; let hash = self.nonce ^ (U256::from(100) * self.gas_price) ^ (U256::from(100_000) * U256::from(self.sender.low_u64())); Transaction { hash: hash.into(), @@ -67,7 +58,6 @@ impl TransactionBuilder { gas_price: self.gas_price, gas: 21_000.into(), sender: self.sender, - insertion_id, mem_usage: self.mem_usage, } } diff --git a/transaction-pool/src/transactions.rs b/transaction-pool/src/transactions.rs index 39fd08e931ca159be3b9835b7e941a25720011c9..edc26b69f4e8b74d497db1158aeb19dbd34568c8 100644 --- a/transaction-pool/src/transactions.rs +++ b/transaction-pool/src/transactions.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -15,28 +15,28 @@ // along with Parity. If not, see . use std::{fmt, mem}; -use std::sync::Arc; use smallvec::SmallVec; use ready::{Ready, Readiness}; use scoring::{self, Scoring}; +use pool::Transaction; #[derive(Debug)] -pub enum AddResult { - Ok(Arc), - TooCheapToEnter(T), +pub enum AddResult { + Ok(T), + TooCheapToEnter(T, S), TooCheap { - old: Arc, + old: T, new: T, }, Replaced { - old: Arc, - new: Arc, + old: T, + new: T, }, PushedOut { - old: Arc, - new: Arc, + old: T, + new: T, }, } @@ -45,7 +45,7 @@ const PER_SENDER: usize = 8; #[derive(Debug)] pub struct Transactions> { // TODO [ToDr] Consider using something that doesn't require shifting all records. - transactions: SmallVec<[Arc; PER_SENDER]>, + transactions: SmallVec<[Transaction; PER_SENDER]>, scores: SmallVec<[S::Score; PER_SENDER]>, } @@ -67,11 +67,11 @@ impl> Transactions { self.transactions.len() } - pub fn iter(&self) -> ::std::slice::Iter> { + pub fn iter(&self) -> ::std::slice::Iter> { self.transactions.iter() } - pub fn worst_and_best(&self) -> Option<((S::Score, Arc), (S::Score, Arc))> { + pub fn worst_and_best(&self) -> Option<((S::Score, Transaction), (S::Score, Transaction))> { let len = self.scores.len(); self.scores.get(0).cloned().map(|best| { let worst = self.scores[len - 1].clone(); @@ -82,7 +82,7 @@ impl> Transactions { }) } - pub fn find_next(&self, tx: &T, scoring: &S) -> Option<(S::Score, Arc)> { + pub fn find_next(&self, tx: &T, scoring: &S) -> Option<(S::Score, Transaction)> { self.transactions.binary_search_by(|old| scoring.compare(old, &tx)).ok().and_then(|index| { let index = index + 1; if index < self.scores.len() { @@ -93,42 +93,44 @@ impl> Transactions { }) } - fn push_cheapest_transaction(&mut self, tx: T, scoring: &S, max_count: usize) -> AddResult { + fn push_cheapest_transaction(&mut self, tx: Transaction, scoring: &S, max_count: usize) -> AddResult, S::Score> { let index = self.transactions.len(); if index == max_count { - AddResult::TooCheapToEnter(tx) + let min_score = self.scores[index - 1].clone(); + AddResult::TooCheapToEnter(tx, min_score) } else { - let shared = Arc::new(tx); - self.transactions.push(shared.clone()); + self.transactions.push(tx.clone()); self.scores.push(Default::default()); scoring.update_scores(&self.transactions, &mut self.scores, scoring::Change::InsertedAt(index)); - AddResult::Ok(shared) + AddResult::Ok(tx) } } - pub fn add(&mut self, tx: T, scoring: &S, max_count: usize) -> AddResult { - let index = match self.transactions.binary_search_by(|old| scoring.compare(old, &tx)) { + pub fn update_scores(&mut self, scoring: &S, event: S::Event) { + scoring.update_scores(&self.transactions, &mut self.scores, scoring::Change::Event(event)); + } + + pub fn add(&mut self, new: Transaction, scoring: &S, max_count: usize) -> AddResult, S::Score> { + let index = match self.transactions.binary_search_by(|old| scoring.compare(old, &new)) { Ok(index) => index, Err(index) => index, }; // Insert at the end. if index == self.transactions.len() { - return self.push_cheapest_transaction(tx, scoring, max_count) + return self.push_cheapest_transaction(new, scoring, max_count) } // Decide if the transaction should replace some other. - match scoring.choose(&self.transactions[index], &tx) { + match scoring.choose(&self.transactions[index], &new) { // New transaction should be rejected scoring::Choice::RejectNew => AddResult::TooCheap { old: self.transactions[index].clone(), - new: tx, + new, }, // New transaction should be kept along with old ones. scoring::Choice::InsertNew => { - let new = Arc::new(tx); - self.transactions.insert(index, new.clone()); self.scores.insert(index, Default::default()); scoring.update_scores(&self.transactions, &mut self.scores, scoring::Change::InsertedAt(index)); @@ -148,7 +150,6 @@ impl> Transactions { }, // New transaction is replacing some other transaction already in the queue. scoring::Choice::ReplaceOld => { - let new = Arc::new(tx); let old = mem::replace(&mut self.transactions[index], new.clone()); scoring.update_scores(&self.transactions, &mut self.scores, scoring::Change::ReplacedAt(index)); @@ -176,7 +177,7 @@ impl> Transactions { return true; } - pub fn cull>(&mut self, ready: &mut R, scoring: &S) -> SmallVec<[Arc; PER_SENDER]> { + pub fn cull>(&mut self, ready: &mut R, scoring: &S) -> SmallVec<[Transaction; PER_SENDER]> { let mut result = SmallVec::new(); if self.is_empty() { return result; @@ -185,13 +186,17 @@ impl> Transactions { let mut first_non_stalled = 0; for tx in &self.transactions { match ready.is_ready(tx) { - Readiness::Stalled => { + Readiness::Stale => { first_non_stalled += 1; }, Readiness::Ready | Readiness::Future => break, } } + if first_non_stalled == 0 { + return result; + } + // reverse the vectors to easily remove first elements. self.transactions.reverse(); self.scores.reverse(); diff --git a/transaction-pool/src/verifier.rs b/transaction-pool/src/verifier.rs index e55a17e91198da8befbcd13e45deb2e864e6f08a..312a3eae3cf58cd82c27ffc9384884ac493661d3 100644 --- a/transaction-pool/src/verifier.rs +++ b/transaction-pool/src/verifier.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/updater/Cargo.toml b/updater/Cargo.toml index 0621fbc676aa040231e752c52a7e7c10708f83b7..8d94680335c9add881daa336829e991db345c657 100644 --- a/updater/Cargo.toml +++ b/updater/Cargo.toml @@ -1,22 +1,30 @@ [package] description = "Parity Updater Service." name = "parity-updater" -version = "1.11.0" +version = "1.12.0" license = "GPL-3.0" authors = ["Parity Technologies "] [dependencies] +keccak-hash = { path = "../util/hash" } +lazy_static = "1.0" log = "0.3" ethabi = "5.1" ethabi-derive = "5.0" ethabi-contract = "5.0" target_info = "0.1" -semver = "0.6" +semver = "0.9" ethcore = { path = "../ethcore" } -ethsync = { path = "../sync" } ethcore-bytes = { path = "../util/bytes" } -ethereum-types = "0.2" +ethcore-sync = { path = "../ethcore/sync" } +ethereum-types = "0.3" parking_lot = "0.5" parity-hash-fetch = { path = "../hash-fetch" } parity-version = { path = "../util/version" } path = { path = "../util/path" } +rand = "0.4" + +[dev-dependencies] +ethcore = { path = "../ethcore", features = ["test-helpers"] } +tempdir = "0.3" +matches = "0.1" diff --git a/updater/res/operations.json b/updater/res/operations.json index 2a14e0f0a01b8aed9f4f89b5cd968340480922f5..b675269f1c866e81da488c5abf2a5f00b45602b0 100644 --- a/updater/res/operations.json +++ b/updater/res/operations.json @@ -556,5 +556,42 @@ ], "payable": false, "type": "function" + }, + { + "name": "ReleaseAdded", + "type": "event", + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "client", + "type": "bytes32" + }, + { + "indexed": true, + "name": "forkBlock", + "type": "uint32" + }, + { + "indexed": false, + "name": "release", + "type": "bytes32" + }, + { + "indexed": false, + "name": "track", + "type": "uint8" + }, + { + "indexed": false, + "name": "semver", + "type": "uint24" + }, + { + "indexed": true, + "name": "critical", + "type": "bool" + } + ] } ] diff --git a/updater/src/lib.rs b/updater/src/lib.rs index c1e19cf1d59b67d3b23620f7013691498228706d..f27d74e7d7a8a932437cf90f4d543eefa43b06d8 100644 --- a/updater/src/lib.rs +++ b/updater/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -19,12 +19,14 @@ extern crate ethabi; extern crate ethcore; extern crate ethcore_bytes as bytes; +extern crate ethcore_sync as sync; extern crate ethereum_types; -extern crate ethsync; +extern crate keccak_hash as hash; extern crate parity_hash_fetch as hash_fetch; extern crate parity_version as version; extern crate parking_lot; extern crate path; +extern crate rand; extern crate semver; extern crate target_info; @@ -33,8 +35,17 @@ extern crate ethabi_contract; #[macro_use] extern crate ethabi_derive; #[macro_use] +extern crate lazy_static; +#[macro_use] extern crate log; +#[cfg(test)] +extern crate tempdir; + +#[cfg(test)] +#[macro_use] +extern crate matches; + mod updater; mod types; mod service; diff --git a/updater/src/service.rs b/updater/src/service.rs index b025eb42eaf455a08caa36f70b3d471e8d3b9b7d..604c01ec76a454fa62076099083b8afc08766081 100644 --- a/updater/src/service.rs +++ b/updater/src/service.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -35,4 +35,3 @@ pub trait Service: Send + Sync { /// Information gathered concerning the release. fn info(&self) -> Option; } - diff --git a/updater/src/types/all.rs b/updater/src/types/all.rs index 7079fb8dedb83dd2b70bd784064428b4a1ac91f1..9dd782683d6dda83dbf9bbd8a8e67ce1c42748c0 100644 --- a/updater/src/types/all.rs +++ b/updater/src/types/all.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/updater/src/types/mod.rs b/updater/src/types/mod.rs index b6d3c6025456940d0c06eb9c0f57a12a14e4e7d8..8fdbcf169df85a9ef8ef38b12060f9c78c107aca 100644 --- a/updater/src/types/mod.rs +++ b/updater/src/types/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -23,4 +23,3 @@ mod version_info; pub use self::all::{ReleaseInfo, OperationsInfo, CapState}; pub use self::release_track::ReleaseTrack; pub use self::version_info::VersionInfo; - diff --git a/updater/src/types/release_track.rs b/updater/src/types/release_track.rs index a1f646805a3ef8b9826b41859d7a8f13bd00bc1e..eefe18d9f20c742e0f181d45f1aba776a45d882e 100644 --- a/updater/src/types/release_track.rs +++ b/updater/src/types/release_track.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -70,7 +70,6 @@ impl From for ReleaseTrack { } } - impl From for u8 { fn from(rt: ReleaseTrack) -> Self { rt as u8 diff --git a/updater/src/types/version_info.rs b/updater/src/types/version_info.rs index 4409153e2ad2e9d4833b4998f126e3805b00b7e8..955be056601e4a3b6384a9291107cdebf0c7d1ce 100644 --- a/updater/src/types/version_info.rs +++ b/updater/src/types/version_info.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/updater/src/updater.rs b/updater/src/updater.rs index ba7a42c875b30fdee34e6815859d724c479f79de..8e9efa0aa14564846c68128b5d1ec532c9e25384 100644 --- a/updater/src/updater.rs +++ b/updater/src/updater.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -14,22 +14,27 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +use std::cmp; use std::fs; use std::io::Write; -use std::path::{PathBuf}; +use std::path::{Path, PathBuf}; use std::sync::{Arc, Weak}; use std::time::{Duration, Instant}; -use ethcore::client::{BlockId, BlockChainClient, ChainNotify}; -use ethsync::{SyncProvider}; +use parking_lot::{Mutex, MutexGuard}; +use rand::{self, Rng}; +use target_info::Target; + +use bytes::Bytes; +use ethcore::BlockNumber; +use ethcore::filter::Filter; +use ethcore::client::{BlockId, BlockChainClient, ChainNotify, ChainRoute}; +use ethereum_types::H256; +use sync::{SyncProvider}; use hash_fetch::{self as fetch, HashFetch}; use path::restrict_permissions_owner; -use service::{Service}; -use target_info::Target; +use service::Service; use types::{ReleaseInfo, OperationsInfo, CapState, VersionInfo, ReleaseTrack}; -use ethereum_types::H256; -use bytes::Bytes; -use parking_lot::Mutex; use version; use_contract!(operations_contract, "Operations", "res/operations.json"); @@ -57,9 +62,13 @@ pub struct UpdatePolicy { /// Which track we should be following. pub track: ReleaseTrack, /// Path for the updates to go. - pub path: String, + pub path: PathBuf, /// Maximum download size. pub max_size: usize, + /// Random update delay range in blocks. + pub max_delay: u64, + /// Number of blocks between each check for updates. + pub frequency: u64, } impl Default for UpdatePolicy { @@ -71,36 +80,74 @@ impl Default for UpdatePolicy { track: ReleaseTrack::Unknown, path: Default::default(), max_size: 128 * 1024 * 1024, + max_delay: 100, + frequency: 20, } } } +/// The current updater status +#[derive(Clone, Debug, PartialEq)] +enum UpdaterStatus { + /// Updater is currently disabled. + Disabled, + /// Updater is currently idle. + Idle, + /// Updater is waiting for block number to fetch a new release. + Waiting { + release: ReleaseInfo, + binary: H256, + block_number: BlockNumber, + }, + /// Updater is fetching a new release. + Fetching { + release: ReleaseInfo, + binary: H256, + retries: u32, + }, + /// Updater failed fetching a new release and it is now backing off until the next retry. + FetchBackoff { + release: ReleaseInfo, + binary: H256, + backoff: (u32, Instant), + }, + /// Updater is ready to update to a new release. + Ready { + release: ReleaseInfo, + }, + /// Updater has installed a new release and can be manually restarted. + Installed { + release: ReleaseInfo, + }, +} + +impl Default for UpdaterStatus { + fn default() -> Self { + UpdaterStatus::Idle + } +} + #[derive(Debug, Default)] struct UpdaterState { latest: Option, - - fetching: Option, - ready: Option, - installed: Option, - capability: CapState, - - disabled: bool, - - backoff: Option<(u32, Instant)>, + status: UpdaterStatus, } /// Service for checking for updates and determining whether we can achieve consensus. -pub struct Updater { +pub struct Updater { // Useful environmental stuff. update_policy: UpdatePolicy, - weak_self: Mutex>, + weak_self: Mutex>>, client: Weak, - sync: Weak, - fetcher: fetch::Client, - operations_contract: operations_contract::Operations, + sync: Option>, + fetcher: F, + operations_client: O, exit_handler: Mutex>>, + time_provider: T, + rng: R, + // Our version info (static) this: VersionInfo, @@ -110,60 +157,74 @@ pub struct Updater { const CLIENT_ID: &'static str = "parity"; -fn client_id_hash() -> H256 { - CLIENT_ID.as_bytes().into() +lazy_static! { + static ref CLIENT_ID_HASH: H256 = CLIENT_ID.as_bytes().into(); } -fn platform() -> String { - if cfg!(target_os = "macos") { - "x86_64-apple-darwin".into() - } else if cfg!(windows) { - "x86_64-pc-windows-msvc".into() - } else if cfg!(target_os = "linux") { - format!("{}-unknown-linux-gnu", Target::arch()) - } else { - version::platform() - } +lazy_static! { + static ref PLATFORM: String = { + if cfg!(target_os = "macos") { + "x86_64-apple-darwin".into() + } else if cfg!(windows) { + "x86_64-pc-windows-msvc".into() + } else if cfg!(target_os = "linux") { + format!("{}-unknown-linux-gnu", Target::arch()) + } else { + version::platform() + } + }; } -fn platform_id_hash() -> H256 { - platform().as_bytes().into() +lazy_static! { + static ref PLATFORM_ID_HASH: H256 = PLATFORM.as_bytes().into(); } -impl Updater { - pub fn new(client: Weak, sync: Weak, update_policy: UpdatePolicy, fetcher: fetch::Client) -> Arc { - let r = Arc::new(Updater { - update_policy: update_policy, - weak_self: Mutex::new(Default::default()), - client: client.clone(), - sync: sync.clone(), - fetcher, - operations_contract: operations_contract::Operations::default(), - exit_handler: Mutex::new(None), - this: VersionInfo::this(), - state: Mutex::new(Default::default()), - }); - *r.weak_self.lock() = Arc::downgrade(&r); - r.poll(); - r +/// Client trait for getting latest release information from operations contract. +/// Useful for mocking in tests. +pub trait OperationsClient: Send + Sync + 'static { + /// Get the latest release operations info for the given track. + fn latest(&self, this: &VersionInfo, track: ReleaseTrack) -> Result; + + /// Fetches the block number when the given release was added, checking the interval [from; latest_block]. + fn release_block_number(&self, from: BlockNumber, release: &ReleaseInfo) -> Option; +} + +/// OperationsClient that delegates calls to the operations contract. +pub struct OperationsContractClient { + operations_contract: operations_contract::Operations, + client: Weak, +} + +impl OperationsContractClient { + fn new( + operations_contract: operations_contract::Operations, + client: Weak, + ) -> OperationsContractClient { + OperationsContractClient { operations_contract, client } } - /// Set a closure to call when we want to restart the client - pub fn set_exit_handler(&self, f: F) where F: Fn() + 'static + Send { - *self.exit_handler.lock() = Some(Box::new(f)); + /// Get the hash of the latest release for the given track + fn latest_hash(&self, track: ReleaseTrack, do_call: &F) -> Result + where F: Fn(Vec) -> Result, String> { + self.operations_contract.functions() + .latest_in_track() + .call(*CLIENT_ID_HASH, u8::from(track), do_call) + .map_err(|e| format!("{:?}", e)) } - fn collect_release_info) -> Result, String>>(&self, release_id: H256, do_call: &T) -> Result { + /// Get release info for the given release + fn release_info(&self, release_id: H256, do_call: &F) -> Result + where F: Fn(Vec) -> Result, String> { let (fork, track, semver, is_critical) = self.operations_contract.functions() .release() - .call(client_id_hash(), release_id, &do_call) + .call(*CLIENT_ID_HASH, release_id, &do_call) .map_err(|e| format!("{:?}", e))?; let (fork, track, semver) = (fork.low_u64(), track.low_u32(), semver.low_u32()); let latest_binary = self.operations_contract.functions() .checksum() - .call(client_id_hash(), release_id, platform_id_hash(), &do_call) + .call(*CLIENT_ID_HASH, release_id, *PLATFORM_ID_HASH, &do_call) .map_err(|e| format!("{:?}", e))?; Ok(ReleaseInfo { @@ -173,39 +234,24 @@ impl Updater { binary: if latest_binary.is_zero() { None } else { Some(latest_binary) }, }) } +} - /// Returns release track of the parity node. - /// `update_policy.track` is the track specified from the command line, whereas `this.track` - /// is the track of the software which is currently run - fn track(&self) -> ReleaseTrack { - match self.update_policy.track { - ReleaseTrack::Unknown => self.this.track, - x => x, - } - } - - fn latest_in_track) -> Result, String>>(&self, track: ReleaseTrack, do_call: &T) -> Result { - self.operations_contract.functions() - .latest_in_track() - .call(client_id_hash(), u8::from(track), do_call) - .map_err(|e| format!("{:?}", e)) - } - - fn collect_latest(&self) -> Result { - if self.track() == ReleaseTrack::Unknown { - return Err(format!("Current executable ({}) is unreleased.", self.this.hash)); +impl OperationsClient for OperationsContractClient { + fn latest(&self, this: &VersionInfo, track: ReleaseTrack) -> Result { + if track == ReleaseTrack::Unknown { + return Err(format!("Current executable ({}) is unreleased.", this.hash)); } let client = self.client.upgrade().ok_or_else(|| "Cannot obtain client")?; let address = client.registry_address("operations".into(), BlockId::Latest).ok_or_else(|| "Cannot get operations contract address")?; let do_call = |data| client.call_contract(BlockId::Latest, address, data).map_err(|e| format!("{:?}", e)); - trace!(target: "updater", "Looking up this_fork for our release: {}/{:?}", CLIENT_ID, self.this.hash); + trace!(target: "updater", "Looking up this_fork for our release: {}/{:?}", CLIENT_ID, this.hash); // get the fork number of this release let this_fork = self.operations_contract.functions() .release() - .call(client_id_hash(), self.this.hash, &do_call) + .call(*CLIENT_ID_HASH, this.hash, &do_call) .ok() .and_then(|(fork, track, _, _)| { let this_track: ReleaseTrack = (track.low_u64() as u8).into(); @@ -216,23 +262,23 @@ impl Updater { }); // get the hash of the latest release in our track - let latest_in_track = self.latest_in_track(self.track(), &do_call)?; + let latest_in_track = self.latest_hash(track, &do_call)?; // get the release info for the latest version in track - let in_track = self.collect_release_info(latest_in_track, &do_call)?; + let in_track = self.release_info(latest_in_track, &do_call)?; let mut in_minor = Some(in_track.clone()); const PROOF: &'static str = "in_minor initialised and assigned with Some; loop breaks if None assigned; qed"; // if the minor version has changed, let's check the minor version on a different track - while in_minor.as_ref().expect(PROOF).version.version.minor != self.this.version.minor { + while in_minor.as_ref().expect(PROOF).version.version.minor != this.version.minor { let track = match in_minor.as_ref().expect(PROOF).version.track { ReleaseTrack::Beta => ReleaseTrack::Stable, ReleaseTrack::Nightly => ReleaseTrack::Beta, _ => { in_minor = None; break; } }; - let latest_in_track = self.latest_in_track(track, &do_call)?; - in_minor = Some(self.collect_release_info(latest_in_track, &do_call)?); + let latest_in_track = self.latest_hash(track, &do_call)?; + in_minor = Some(self.release_info(latest_in_track, &do_call)?); } let fork = self.operations_contract.functions() @@ -248,196 +294,954 @@ impl Updater { }) } + fn release_block_number(&self, from: BlockNumber, release: &ReleaseInfo) -> Option { + let client = self.client.upgrade()?; + let address = client.registry_address("operations".into(), BlockId::Latest)?; + + let event = self.operations_contract.events().release_added(); + + let topics = event.create_filter(Some(*CLIENT_ID_HASH), Some(release.fork.into()), Some(release.is_critical)); + let topics = vec![topics.topic0, topics.topic1, topics.topic2, topics.topic3]; + let topics = topics.into_iter().map(Into::into).map(Some).collect(); + + let filter = Filter { + from_block: BlockId::Number(from), + to_block: BlockId::Latest, + address: Some(vec![address]), + topics: topics, + limit: None, + }; + + client.logs(filter) + .iter() + .filter_map(|log| { + let event = event.parse_log((log.topics.clone(), log.data.clone()).into()).ok()?; + let version_info = VersionInfo::from_raw(event.semver.low_u32(), event.track.low_u32() as u8, event.release.into()); + if version_info == release.version { + Some(log.block_number) + } else { + None + } + }) + .last() + } +} + +/// Trait to provide current time. Useful for mocking in tests. +pub trait TimeProvider: Send + Sync + 'static { + /// Returns an instant corresponding to "now". + fn now(&self) -> Instant; +} + +/// TimeProvider implementation that delegates calls to std::time. +pub struct StdTimeProvider; + +impl TimeProvider for StdTimeProvider { + fn now(&self) -> Instant { + Instant::now() + } +} + +/// Trait to generate a random number within a given range. +/// Useful for mocking in tests. +pub trait GenRange: Send + Sync + 'static { + /// Generate a random value in the range [low, high), i.e. inclusive of low and exclusive of high. + fn gen_range(&self, low: u64, high: u64) -> u64; +} + +/// GenRange implementation that uses a rand::thread_rng for randomness. +pub struct ThreadRngGenRange; + +impl GenRange for ThreadRngGenRange { + fn gen_range(&self, low: u64, high: u64) -> u64 { + rand::thread_rng().gen_range(low, high) + } +} + +impl Updater { + pub fn new( + client: Weak, + sync: Weak, + update_policy: UpdatePolicy, + fetcher: fetch::Client, + ) -> Arc { + let r = Arc::new(Updater { + update_policy: update_policy, + weak_self: Mutex::new(Default::default()), + client: client.clone(), + sync: Some(sync.clone()), + fetcher, + operations_client: OperationsContractClient::new( + operations_contract::Operations::default(), + client.clone()), + exit_handler: Mutex::new(None), + this: VersionInfo::this(), + time_provider: StdTimeProvider, + rng: ThreadRngGenRange, + state: Mutex::new(Default::default()), + }); + *r.weak_self.lock() = Arc::downgrade(&r); + r.poll(); + r + } + fn update_file_name(v: &VersionInfo) -> String { - format!("parity-{}.{}.{}-{:?}", v.version.major, v.version.minor, v.version.patch, v.hash) + format!("parity-{}.{}.{}-{:x}", v.version.major, v.version.minor, v.version.patch, v.hash) + } +} + +impl Updater { + /// Set a closure to call when we want to restart the client + pub fn set_exit_handler(&self, g: G) where G: Fn() + 'static + Send { + *self.exit_handler.lock() = Some(Box::new(g)); + } + + /// Returns release track of the parity node. + /// `update_policy.track` is the track specified from the command line, whereas `this.track` + /// is the track of the software which is currently run + fn track(&self) -> ReleaseTrack { + match self.update_policy.track { + ReleaseTrack::Unknown => self.this.track, + x => x, + } } fn updates_path(&self, name: &str) -> PathBuf { - let mut dest = PathBuf::from(self.update_policy.path.clone()); - dest.push(name); - dest - } - - fn fetch_done(&self, result: Result) { - // old below - (|| -> Result<(), (String, bool)> { - let auto = { - let mut s = self.state.lock(); - let fetched = s.fetching.take().unwrap(); - let dest = self.updates_path(&Self::update_file_name(&fetched.version)); - if !dest.exists() { - let b = match result { - Ok(b) => { - s.backoff = None; - b - }, - Err(e) => { - let mut n = s.backoff.map(|b| b.0 + 1).unwrap_or(1); - s.backoff = Some((n, Instant::now() + Duration::from_secs(2usize.pow(n) as u64))); - - return Err((format!("Unable to fetch update ({}): {:?}", fetched.version, e), false)); - }, + self.update_policy.path.join(name) + } + + fn on_fetch(&self, latest: &OperationsInfo, res: Result) { + let mut state = self.state.lock(); + + // Bail out if the latest release has changed in the meantime + if state.latest.as_ref() != Some(&latest) { + return; + } + + // The updated status should be set to fetching + if let UpdaterStatus::Fetching { ref release, binary, retries } = state.status.clone() { + match res { + // We've successfully fetched the binary + Ok(path) => { + let setup = |path: &Path| -> Result<(), String> { + let dest = self.updates_path(&Updater::update_file_name(&release.version)); + if !dest.exists() { + info!(target: "updater", "Fetched latest version ({}) OK to {}", release.version, path.display()); + fs::create_dir_all(dest.parent().expect("at least one thing pushed; qed")).map_err(|e| format!("Unable to create updates path: {:?}", e))?; + fs::copy(path, &dest).map_err(|e| format!("Unable to copy update: {:?}", e))?; + restrict_permissions_owner(&dest, false, true).map_err(|e| format!("Unable to update permissions: {}", e))?; + info!(target: "updater", "Copied updated binary to {}", dest.display()); + } + + Ok(()) }; - info!(target: "updater", "Fetched latest version ({}) OK to {}", fetched.version, b.display()); - fs::create_dir_all(dest.parent().expect("at least one thing pushed; qed")).map_err(|e| (format!("Unable to create updates path: {:?}", e), true))?; - fs::copy(&b, &dest).map_err(|e| (format!("Unable to copy update: {:?}", e), true))?; - restrict_permissions_owner(&dest, false, true).map_err(|e| (format!("Unable to update permissions: {}", e), true))?; - info!(target: "updater", "Installed updated binary to {}", dest.display()); - } - let auto = match self.update_policy.filter { - UpdateFilter::All => true, - UpdateFilter::Critical if fetched.is_critical /* TODO: or is on a bad fork */ => true, - _ => false, + // There was a fatal error setting up the update, disable the updater + if let Err(err) = setup(&path) { + state.status = UpdaterStatus::Disabled; + warn!("{}", err); + } else { + state.status = UpdaterStatus::Ready { release: release.clone() }; + self.updater_step(state); + } + }, + // There was an error fetching the update, apply a backoff delay before retrying + Err(err) => { + let delay = 2usize.pow(retries) as u64; + // cap maximum backoff to 1 day + let delay = cmp::min(delay, 24 * 60 * 60); + let backoff = (retries, self.time_provider.now() + Duration::from_secs(delay)); + + state.status = UpdaterStatus::FetchBackoff { release: release.clone(), backoff, binary }; + + warn!("Unable to fetch update ({}): {:?}, retrying in {} seconds.", release.version, err, delay); + }, + } + } + } + + fn execute_upgrade(&self, mut state: MutexGuard) -> bool { + if let UpdaterStatus::Ready { ref release } = state.status.clone() { + let file = Updater::update_file_name(&release.version); + let path = self.updates_path("latest"); + + // TODO: creating then writing is a bit fragile. would be nice to make it atomic. + if let Err(err) = fs::File::create(&path).and_then(|mut f| f.write_all(file.as_bytes())) { + state.status = UpdaterStatus::Disabled; + + warn!(target: "updater", "Unable to create soft-link for update {:?}", err); + return false; + } + + info!(target: "updater", "Completed upgrade to {}", &release.version); + state.status = UpdaterStatus::Installed { release: release.clone() }; + + match *self.exit_handler.lock() { + Some(ref h) => (*h)(), + None => info!(target: "updater", "Update installed, ready for restart."), + } + + return true; + }; + + warn!(target: "updater", "Execute upgrade called when no upgrade ready."); + false + } + + fn updater_step(&self, mut state: MutexGuard) { + let current_block_number = self.client.upgrade().map_or(0, |c| c.block_number(BlockId::Latest).unwrap_or(0)); + + if let Some(latest) = state.latest.clone() { + let fetch = |latest, binary| { + info!(target: "updater", "Attempting to get parity binary {}", binary); + let weak_self = self.weak_self.lock().clone(); + let f = move |res: Result| { + if let Some(this) = weak_self.upgrade() { + this.on_fetch(&latest, res) + } }; - s.ready = Some(fetched); - auto + + self.fetcher.fetch( + binary, + fetch::Abort::default().with_max_size(self.update_policy.max_size), + Box::new(f)); }; - if auto { - // will lock self.state, so ensure it's outside of previous block. - self.execute_upgrade(); + + match state.status.clone() { + // updater is disabled + UpdaterStatus::Disabled => {}, + // the update has already been installed + UpdaterStatus::Installed { ref release, .. } if *release == latest.track => {}, + // we're currently fetching this update + UpdaterStatus::Fetching { ref release, .. } if *release == latest.track => {}, + // the fetch has failed and we're backing off the next retry + UpdaterStatus::FetchBackoff { ref release, backoff, .. } if *release == latest.track && self.time_provider.now() < backoff.1 => {}, + // we're delaying the update until the given block number + UpdaterStatus::Waiting { ref release, block_number, .. } if *release == latest.track && current_block_number < block_number => {}, + // we're at (or past) the block that triggers the update, let's fetch the binary + UpdaterStatus::Waiting { ref release, block_number, binary } if *release == latest.track && current_block_number >= block_number => { + info!(target: "updater", "Update for binary {} triggered", binary); + + state.status = UpdaterStatus::Fetching { release: release.clone(), binary, retries: 1 }; + fetch(latest, binary); + }, + // we're ready to retry the fetch after we applied a backoff for the previous failure + UpdaterStatus::FetchBackoff { ref release, backoff, binary } if *release == latest.track && self.time_provider.now() >= backoff.1 => { + state.status = UpdaterStatus::Fetching { release: release.clone(), binary, retries: backoff.0 + 1 }; + fetch(latest, binary); + }, + // the update is ready to be installed + UpdaterStatus::Ready { ref release } if *release == latest.track => { + let auto = match self.update_policy.filter { + UpdateFilter::All => true, + UpdateFilter::Critical if release.is_critical /* TODO: or is on a bad fork */ => true, + _ => false, + }; + + if auto { + self.execute_upgrade(state); + } + }, + // this is the default case that does the initial triggering to update. we can reach this case by being + // `Idle` but also if the latest release is updated, regardless of the state we're in (except if the + // updater is in the `Disabled` state). if we push a bad update (e.g. wrong hashes or download url) + // clients might eventually be on a really long backoff state for that release, but as soon a new + // release is pushed we'll fall through to the default case. + _ => { + if let Some(binary) = latest.track.binary { + let running_later = latest.track.version.version < self.version_info().version; + let running_latest = latest.track.version.hash == self.version_info().hash; + + // Bail out if we're already running the latest version or a later one + if running_later || running_latest { + return; + } + + let path = self.updates_path(&Updater::update_file_name(&latest.track.version)); + if path.exists() { + info!(target: "updater", "Already fetched binary."); + state.status = UpdaterStatus::Ready { release: latest.track.clone() }; + self.updater_step(state); + + } else if self.update_policy.enable_downloading { + let update_block_number = { + let max_delay = if latest.fork >= current_block_number { + cmp::min(latest.fork - current_block_number, self.update_policy.max_delay) + } else { + self.update_policy.max_delay + }; + + let from = current_block_number.saturating_sub(max_delay); + match self.operations_client.release_block_number(from, &latest.track) { + Some(block_number) => { + let delay = self.rng.gen_range(0, max_delay); + block_number.saturating_add(delay) + }, + None => current_block_number, + } + }; + + state.status = UpdaterStatus::Waiting { release: latest.track.clone(), binary, block_number: update_block_number }; + + if update_block_number > current_block_number { + info!(target: "updater", "Update for binary {} will be triggered at block {}", binary, update_block_number); + } else { + self.updater_step(state); + } + } + } + }, } - Ok(()) - })().unwrap_or_else(|(e, fatal)| { self.state.lock().disabled = fatal; warn!("{}", e); }); + } } fn poll(&self) { trace!(target: "updater", "Current release is {} ({:?})", self.this, self.this.hash); // We rely on a secure state. Bail if we're unsure about it. - if self.client.upgrade().map_or(true, |s| !s.chain_info().security_level().is_full()) { + if self.client.upgrade().map_or(true, |c| !c.chain_info().security_level().is_full()) { return; } - let current_number = self.client.upgrade().map_or(0, |c| c.block_number(BlockId::Latest).unwrap_or(0)); - - let mut capability = CapState::Unknown; - let latest = self.collect_latest().ok(); - if let Some(ref latest) = latest { - trace!(target: "updater", "Latest release in our track is v{} it is {}critical ({} binary is {})", - latest.track.version, - if latest.track.is_critical {""} else {"non-"}, - &platform(), - if let Some(ref b) = latest.track.binary { - format!("{}", b) - } else { - "unreleased".into() - } - ); - let mut s = self.state.lock(); - let running_later = latest.track.version.version < self.version_info().version; - let running_latest = latest.track.version.hash == self.version_info().hash; - let already_have_latest = s.installed.as_ref().or(s.ready.as_ref()).map_or(false, |t| *t == latest.track); - - if !s.disabled && self.update_policy.enable_downloading && !running_later && !running_latest && !already_have_latest { - if let Some(b) = latest.track.binary { - if s.fetching.is_none() { - if self.updates_path(&Self::update_file_name(&latest.track.version)).exists() { - info!(target: "updater", "Already fetched binary."); - s.fetching = Some(latest.track.clone()); - drop(s); - self.fetch_done(Ok(PathBuf::new())); - } else { - if s.backoff.iter().all(|&(_, instant)| Instant::now() >= instant) { - info!(target: "updater", "Attempting to get parity binary {}", b); - s.fetching = Some(latest.track.clone()); - drop(s); - let weak_self = self.weak_self.lock().clone(); - let f = move |r: Result| if let Some(this) = weak_self.upgrade() { this.fetch_done(r) }; - let a = fetch::Abort::default().with_max_size(self.update_policy.max_size); - self.fetcher.fetch(b, a, Box::new(f)); - } - } - } - } - } - trace!(target: "updater", "Fork: this/current/latest/latest-known: {}/#{}/#{}/#{}", match latest.this_fork { Some(f) => format!("#{}", f), None => "unknown".into(), }, current_number, latest.track.fork, latest.fork); - - if let Some(this_fork) = latest.this_fork { - if this_fork < latest.fork { - // We're behind the latest fork. Now is the time to be upgrading; perhaps we're too late... - if let Some(c) = self.client.upgrade() { - let current_number = c.block_number(BlockId::Latest).unwrap_or(0); - if current_number >= latest.fork - 1 { - // We're at (or past) the last block we can import. Disable the client. - if self.update_policy.require_consensus { + // Only check for updates every n blocks + let current_block_number = self.client.upgrade().map_or(0, |c| c.block_number(BlockId::Latest).unwrap_or(0)); + if current_block_number % cmp::max(self.update_policy.frequency, 1) != 0 { + return; + } + + let mut state = self.state.lock(); + + // Get the latest available release + let latest = self.operations_client.latest(&self.this, self.track()).ok(); + + if let Some(latest) = latest { + // Update current capability + state.capability = match latest.this_fork { + // We're behind the latest fork. Now is the time to be upgrading, perhaps we're too late... + Some(this_fork) if this_fork < latest.fork => { + if current_block_number >= latest.fork - 1 { + // We're at (or past) the last block we can import. Disable the client. + if self.update_policy.require_consensus { + if let Some(c) = self.client.upgrade() { c.disable(); } - capability = CapState::IncapableSince(latest.fork); - } else { - capability = CapState::CapableUntil(latest.fork); } + + CapState::IncapableSince(latest.fork) + } else { + CapState::CapableUntil(latest.fork) } - } else { - capability = CapState::Capable; - } - } - } + }, + Some(_) => CapState::Capable, + None => CapState::Unknown, + }; + + // There's a new release available + if state.latest.as_ref() != Some(&latest) { + trace!(target: "updater", "Latest release in our track is v{} it is {}critical ({} binary is {})", + latest.track.version, + if latest.track.is_critical {""} else {"non-"}, + *PLATFORM, + latest.track.binary.map(|b| format!("{}", b)).unwrap_or("unreleased".into())); - let mut s = self.state.lock(); + trace!(target: "updater", "Fork: this/current/latest/latest-known: {}/#{}/#{}/#{}", + latest.this_fork.map(|f| format!("#{}", f)).unwrap_or("unknown".into()), + current_block_number, + latest.track.fork, + latest.fork); - if s.latest != latest { - s.backoff = None; + // Update latest release + state.latest = Some(latest); + } } - s.latest = latest; - s.capability = capability; + self.updater_step(state); } } impl ChainNotify for Updater { - fn new_blocks(&self, _imported: Vec, _invalid: Vec, _enacted: Vec, _retracted: Vec, _sealed: Vec, _proposed: Vec, _duration: u64) { - match (self.client.upgrade(), self.sync.upgrade()) { + fn new_blocks(&self, _imported: Vec, _invalid: Vec, _route: ChainRoute, _sealed: Vec, _proposed: Vec, _duration: Duration) { + match (self.client.upgrade(), self.sync.as_ref().and_then(Weak::upgrade)) { (Some(ref c), Some(ref s)) if !s.status().is_syncing(c.queue_info()) => self.poll(), _ => {}, } } } -impl Service for Updater { +impl Service for Updater { fn capability(&self) -> CapState { self.state.lock().capability } fn upgrade_ready(&self) -> Option { - self.state.lock().ready.clone() + match self.state.lock().status { + UpdaterStatus::Ready { ref release, .. } => Some(release.clone()), + _ => None, + } } fn execute_upgrade(&self) -> bool { - let mut s = self.state.lock(); - let ready = match s.ready.take() { - Some(ready) => ready, - None => { - warn!(target: "updater", "Execute upgrade called when no upgrade ready."); - return false; + let state = self.state.lock(); + self.execute_upgrade(state) + } + + fn version_info(&self) -> VersionInfo { + self.this.clone() + } + + fn info(&self) -> Option { + self.state.lock().latest.clone() + } +} + +#[cfg(test)] +pub mod tests { + use std::fs::File; + use std::io::Read; + use std::sync::Arc; + use semver::Version; + use tempdir::TempDir; + use ethcore::client::{TestBlockChainClient, EachBlockWith}; + use self::fetch::Error; + use super::*; + + #[derive(Clone)] + struct FakeOperationsClient { + result: Arc, Option)>>, + } + + impl FakeOperationsClient { + fn new() -> FakeOperationsClient { + FakeOperationsClient { result: Arc::new(Mutex::new((None, None))) } + } + + fn set_result(&self, operations_info: Option, release_block_number: Option) { + let mut result = self.result.lock(); + result.0 = operations_info; + result.1 = release_block_number; + } + } + + impl OperationsClient for FakeOperationsClient { + fn latest(&self, _this: &VersionInfo, _track: ReleaseTrack) -> Result { + self.result.lock().0.clone().ok_or("unavailable".into()) + } + + fn release_block_number(&self, _from: BlockNumber, _release: &ReleaseInfo) -> Option { + self.result.lock().1.clone() + } + } + + #[derive(Clone)] + struct FakeFetch { + on_done: Arc) + Send>>>>, + } + + impl FakeFetch { + fn new() -> FakeFetch { + FakeFetch { on_done: Arc::new(Mutex::new(None)) } + } + + fn trigger(&self, result: Option) { + if let Some(ref on_done) = *self.on_done.lock() { + on_done(result.ok_or(Error::NoResolution)) } - }; + } + } - let p = Self::update_file_name(&ready.version); - let n = self.updates_path("latest"); + impl HashFetch for FakeFetch { + fn fetch(&self, _hash: H256, _abort: fetch::Abort, on_done: Box) + Send>) { + *self.on_done.lock() = Some(on_done); + } + } - // TODO: creating then writing is a bit fragile. would be nice to make it atomic. - if let Err(e) = fs::File::create(&n).and_then(|mut f| f.write_all(p.as_bytes())) { - s.ready = Some(ready); - warn!(target: "updater", "Unable to create soft-link for update {:?}", e); - return false; + #[derive(Clone)] + struct FakeTimeProvider { + result: Arc>, + } + + impl FakeTimeProvider { + fn new() -> FakeTimeProvider { + FakeTimeProvider { result: Arc::new(Mutex::new(Instant::now())) } } - info!(target: "updater", "Completed upgrade to {}", &ready.version); - s.installed = Some(ready); - match *self.exit_handler.lock() { - Some(ref h) => (*h)(), - None => info!(target: "updater", "Update installed; ready for restart."), + fn set_result(&self, result: Instant) { + *self.result.lock() = result; } + } - true + impl TimeProvider for FakeTimeProvider { + fn now(&self) -> Instant { + *self.result.lock() + } } - fn version_info(&self) -> VersionInfo { - self.this.clone() + #[derive(Clone)] + struct FakeGenRange { + result: Arc>, } - fn info(&self) -> Option { - self.state.lock().latest.clone() + impl FakeGenRange { + fn new() -> FakeGenRange { + FakeGenRange { result: Arc::new(Mutex::new(0)) } + } + + fn set_result(&self, result: u64) { + *self.result.lock() = result; + } + } + + impl GenRange for FakeGenRange { + fn gen_range(&self, _low: u64, _high: u64) -> u64 { + *self.result.lock() + } + } + + type TestUpdater = Updater; + + fn setup(update_policy: UpdatePolicy) -> ( + Arc, + Arc, + FakeOperationsClient, + FakeFetch, + FakeTimeProvider, + FakeGenRange) { + + let client = Arc::new(TestBlockChainClient::new()); + let weak_client = Arc::downgrade(&client); + + let operations_client = FakeOperationsClient::new(); + let fetcher = FakeFetch::new(); + let time_provider = FakeTimeProvider::new(); + let rng = FakeGenRange::new(); + + let this = VersionInfo { + track: ReleaseTrack::Beta, + version: Version::parse("1.0.0").unwrap(), + hash: 0.into(), + }; + + let updater = Arc::new(Updater { + update_policy: update_policy, + weak_self: Mutex::new(Default::default()), + client: weak_client, + sync: None, + fetcher: fetcher.clone(), + operations_client: operations_client.clone(), + exit_handler: Mutex::new(None), + this: this, + time_provider: time_provider.clone(), + rng: rng.clone(), + state: Mutex::new(Default::default()), + }); + + *updater.weak_self.lock() = Arc::downgrade(&updater); + + (client, updater, operations_client, fetcher, time_provider, rng) + } + + fn update_policy() -> (UpdatePolicy, TempDir) { + let tempdir = TempDir::new("").unwrap(); + + let update_policy = UpdatePolicy { + path: tempdir.path().into(), + enable_downloading: true, + max_delay: 10, + frequency: 1, + ..Default::default() + }; + + (update_policy, tempdir) + } + + fn new_upgrade(version: &str) -> (VersionInfo, ReleaseInfo, OperationsInfo) { + let latest_version = VersionInfo { + track: ReleaseTrack::Beta, + version: Version::parse(version).unwrap(), + hash: 1.into(), + }; + + let latest_release = ReleaseInfo { + version: latest_version.clone(), + is_critical: false, + fork: 0, + binary: Some(0.into()), + }; + + let latest = OperationsInfo { + fork: 0, + this_fork: Some(0), + track: latest_release.clone(), + minor: None, + }; + + (latest_version, latest_release, latest) + } + + #[test] + fn should_stay_idle_when_no_release() { + let (update_policy, _) = update_policy(); + let (_client, updater, _, _, ..) = setup(update_policy); + + assert_eq!(updater.state.lock().status, UpdaterStatus::Idle); + updater.poll(); + assert_eq!(updater.state.lock().status, UpdaterStatus::Idle); + } + + #[test] + fn should_update_on_new_release() { + let (update_policy, tempdir) = update_policy(); + let (_client, updater, operations_client, fetcher, ..) = setup(update_policy); + let (latest_version, latest_release, latest) = new_upgrade("1.0.1"); + + // mock operations contract with a new version + operations_client.set_result(Some(latest.clone()), None); + + // we start in idle state and with no information regarding the latest release + assert_eq!(updater.state.lock().latest, None); + assert_eq!(updater.state.lock().status, UpdaterStatus::Idle); + + updater.poll(); + + // after the first poll the latest release should be set to the one we're mocking and the updater should be + // fetching it + assert_eq!(updater.state.lock().latest, Some(latest)); + assert_matches!( + updater.state.lock().status, + UpdaterStatus::Fetching { ref release, retries, .. } if *release == latest_release && retries == 1); + + // mock fetcher with update binary and trigger the fetch + let update_file = tempdir.path().join("parity"); + File::create(update_file.clone()).unwrap(); + fetcher.trigger(Some(update_file)); + + // after the fetch finishes the upgrade should be ready to install + assert_eq!(updater.state.lock().status, UpdaterStatus::Ready { release: latest_release.clone() }); + assert_eq!(updater.upgrade_ready(), Some(latest_release.clone())); + + // the current update_policy doesn't allow updating automatically, but we can trigger the update manually + ::execute_upgrade(&*updater); + + assert_eq!(updater.state.lock().status, UpdaterStatus::Installed { release: latest_release }); + + // the final binary should exist in the updates folder and the 'latest' file should be updated to point to it + let updated_binary = tempdir.path().join(Updater::update_file_name(&latest_version)); + let latest_file = tempdir.path().join("latest"); + + assert!(updated_binary.exists()); + assert!(latest_file.exists()); + + let mut latest_file_content = String::new(); + File::open(latest_file).unwrap().read_to_string(&mut latest_file_content).unwrap(); + + assert_eq!(latest_file_content, updated_binary.file_name().and_then(|n| n.to_str()).unwrap()); + } + + #[test] + fn should_randomly_delay_new_updates() { + let (update_policy, _) = update_policy(); + let (client, updater, operations_client, _, _, rng) = setup(update_policy); + + let (_, latest_release, latest) = new_upgrade("1.0.1"); + operations_client.set_result(Some(latest.clone()), Some(0)); + + rng.set_result(5); + + updater.poll(); + + // the update should be delayed for 5 blocks + assert_matches!( + updater.state.lock().status, + UpdaterStatus::Waiting { ref release, block_number, .. } if *release == latest_release && block_number == 5); + + client.add_blocks(1, EachBlockWith::Nothing); + updater.poll(); + + // we should still be in the waiting state after we push one block + assert_matches!( + updater.state.lock().status, + UpdaterStatus::Waiting { ref release, block_number, .. } if *release == latest_release && block_number == 5); + + client.add_blocks(5, EachBlockWith::Nothing); + updater.poll(); + + // after we're past the delay the status should switch to fetching + assert_matches!( + updater.state.lock().status, + UpdaterStatus::Fetching { ref release, .. } if *release == latest_release); + } + + #[test] + fn should_not_delay_old_updates() { + let (update_policy, _) = update_policy(); + let (client, updater, operations_client, ..) = setup(update_policy); + client.add_blocks(100, EachBlockWith::Nothing); + + let (_, latest_release, latest) = new_upgrade("1.0.1"); + operations_client.set_result(Some(latest.clone()), Some(0)); + + updater.poll(); + + // the update should not be delayed since it's older than the maximum delay + // the update was at block 0 (100 blocks ago), and the maximum delay is 10 blocks + assert_matches!( + updater.state.lock().status, + UpdaterStatus::Fetching { ref release, .. } if *release == latest_release); + } + + #[test] + fn should_check_for_updates_with_configured_frequency() { + let (mut update_policy, _) = update_policy(); + update_policy.frequency = 2; + + let (client, updater, operations_client, _, _, rng) = setup(update_policy); + let (_, latest_release, latest) = new_upgrade("1.0.1"); + operations_client.set_result(Some(latest.clone()), Some(0)); + rng.set_result(5); + + client.add_blocks(1, EachBlockWith::Nothing); + updater.poll(); + + // the updater should stay idle since we only check for updates every other block (odd blocks in this case) + assert_eq!(updater.state.lock().status, UpdaterStatus::Idle); + + client.add_blocks(1, EachBlockWith::Nothing); + updater.poll(); + + // after adding a block we check for a new update and trigger the random delay (of 5 blocks) + assert_matches!( + updater.state.lock().status, + UpdaterStatus::Waiting { ref release, block_number, .. } if *release == latest_release && block_number == 5); + } + + #[test] + fn should_backoff_retry_when_update_fails() { + let (update_policy, tempdir) = update_policy(); + let (_client, updater, operations_client, fetcher, time_provider, ..) = setup(update_policy); + let (_, latest_release, latest) = new_upgrade("1.0.1"); + + // mock operations contract with a new version + operations_client.set_result(Some(latest.clone()), None); + + let mut now = Instant::now(); + time_provider.set_result(now); + + updater.poll(); + fetcher.trigger(None); + + // we triggered the fetcher with an error result so the updater should backoff any retry + assert_matches!( + updater.state.lock().status, + UpdaterStatus::FetchBackoff { ref release, ref backoff, .. } if *release == latest_release && backoff.0 == 1); + + now += Duration::from_secs(1); + time_provider.set_result(now); + updater.poll(); + + // if we don't wait for the elapsed time the updater status should stay the same + assert_matches!( + updater.state.lock().status, + UpdaterStatus::FetchBackoff { ref release, ref backoff, .. } if *release == latest_release && backoff.0 == 1); + + now += Duration::from_secs(1); + time_provider.set_result(now); + updater.poll(); + fetcher.trigger(None); + + // the backoff time has elapsed so we retried again (and failed) + assert_matches!( + updater.state.lock().status, + UpdaterStatus::FetchBackoff { ref release, ref backoff, .. } if *release == latest_release && backoff.0 == 2); + + now += Duration::from_secs(4); + time_provider.set_result(now); + updater.poll(); + + let update_file = tempdir.path().join("parity"); + File::create(update_file.clone()).unwrap(); + fetcher.trigger(Some(update_file)); + + // after setting up the mocked fetch and waiting for the backoff period the update should succeed + assert_eq!(updater.state.lock().status, UpdaterStatus::Ready { release: latest_release }); + } + + #[test] + fn should_quit_backoff_on_new_release() { + let (update_policy, tempdir) = update_policy(); + let (_client, updater, operations_client, fetcher, ..) = setup(update_policy); + let (_, latest_release, latest) = new_upgrade("1.0.1"); + + // mock operations contract with a new version + operations_client.set_result(Some(latest.clone()), None); + + updater.poll(); + fetcher.trigger(None); + + // we triggered the fetcher with an error result so the updater should backoff any retry + assert_matches!( + updater.state.lock().status, + UpdaterStatus::FetchBackoff { ref release, ref backoff, .. } if *release == latest_release && backoff.0 == 1); + + // mock new working release and trigger the fetch afterwards + let (_, latest_release, latest) = new_upgrade("1.0.2"); + operations_client.set_result(Some(latest.clone()), None); + let update_file = tempdir.path().join("parity"); + File::create(update_file.clone()).unwrap(); + + updater.poll(); + fetcher.trigger(Some(update_file)); + + // a new release should short-circuit the backoff + assert_eq!(updater.state.lock().status, UpdaterStatus::Ready { release: latest_release }); + } + + #[test] + fn should_detect_already_downloaded_releases() { + let (update_policy, tempdir) = update_policy(); + let (_client, updater, operations_client, ..) = setup(update_policy); + let (latest_version, latest_release, latest) = new_upgrade("1.0.1"); + + // mock operations contract with a new version + operations_client.set_result(Some(latest.clone()), None); + + // mock final update file + let update_file = tempdir.path().join(Updater::update_file_name(&latest_version)); + File::create(update_file.clone()).unwrap(); + + updater.poll(); + + // after checking for a new update we immediately declare it as ready since it already exists on disk + // there was no need to trigger the fetch + assert_eq!(updater.state.lock().status, UpdaterStatus::Ready { release: latest_release }); + } + + #[test] + fn should_stay_disabled_after_fatal_error() { + let (update_policy, tempdir) = update_policy(); + let (client, updater, operations_client, fetcher, ..) = setup(update_policy); + let (_, _, latest) = new_upgrade("1.0.1"); + + // mock operations contract with a new version + operations_client.set_result(Some(latest.clone()), None); + + updater.poll(); + // trigger the fetch but don't create the file on-disk. this should lead to a fatal error that disables the updater + let update_file = tempdir.path().join("parity"); + fetcher.trigger(Some(update_file)); + + assert_eq!(updater.state.lock().status, UpdaterStatus::Disabled); + + client.add_blocks(100, EachBlockWith::Nothing); + updater.poll(); + + // the updater should stay disabled after new blocks are pushed + assert_eq!(updater.state.lock().status, UpdaterStatus::Disabled); + + let (_, _, latest) = new_upgrade("1.0.2"); + operations_client.set_result(Some(latest.clone()), None); + + updater.poll(); + + // the updater should stay disabled after a new release is pushed + assert_eq!(updater.state.lock().status, UpdaterStatus::Disabled); + } + + #[test] + fn should_ignore_current_fetch_on_new_release() { + let (update_policy, _) = update_policy(); + let (_client, updater, operations_client, fetcher, ..) = setup(update_policy); + let (_, latest_release, latest) = new_upgrade("1.0.1"); + + // mock operations contract with a new version + operations_client.set_result(Some(latest.clone()), None); + + updater.poll(); + + assert_matches!( + updater.state.lock().status, + UpdaterStatus::Fetching { ref release, .. } if *release == latest_release); + + let (_, latest_release, latest) = new_upgrade("1.0.2"); + operations_client.set_result(Some(latest.clone()), None); + fetcher.trigger(None); + updater.poll(); + + // even though we triggered the previous fetch with an error, the current state was updated to fetch the new + // release, and the previous fetch is ignored + assert_matches!( + updater.state.lock().status, + UpdaterStatus::Fetching { ref release, .. } if *release == latest_release); + } + + #[test] + fn should_auto_install_updates_if_update_policy_allows() { + let (mut update_policy, tempdir) = update_policy(); + update_policy.filter = UpdateFilter::All; + let (_client, updater, operations_client, fetcher, ..) = setup(update_policy); + let (latest_version, latest_release, latest) = new_upgrade("1.0.1"); + + // mock operations contract with a new version + operations_client.set_result(Some(latest.clone()), None); + + // we start in idle state and with no information regarding the latest release + assert_eq!(updater.state.lock().latest, None); + assert_eq!(updater.state.lock().status, UpdaterStatus::Idle); + + updater.poll(); + + // mock fetcher with update binary and trigger the fetch + let update_file = tempdir.path().join("parity"); + File::create(update_file.clone()).unwrap(); + fetcher.trigger(Some(update_file)); + + // the update is auto installed since the update policy allows it + assert_eq!(updater.state.lock().status, UpdaterStatus::Installed { release: latest_release }); + + // the final binary should exist in the updates folder and the 'latest' file should be updated to point to it + let updated_binary = tempdir.path().join(Updater::update_file_name(&latest_version)); + let latest_file = tempdir.path().join("latest"); + + assert!(updated_binary.exists()); + assert!(latest_file.exists()); + + let mut latest_file_content = String::new(); + File::open(latest_file).unwrap().read_to_string(&mut latest_file_content).unwrap(); + + assert_eq!(latest_file_content, updated_binary.file_name().and_then(|n| n.to_str()).unwrap()); + } + + #[test] + fn should_update_capability() { + let (update_policy, _tempdir) = update_policy(); + let (client, updater, operations_client, _, ..) = setup(update_policy); + let (_, _, mut latest) = new_upgrade("1.0.1"); + + // mock operations contract with a new version + operations_client.set_result(Some(latest.clone()), None); + + // we start with no information regarding our node's capabilities + assert_eq!(updater.state.lock().capability, CapState::Unknown); + + updater.poll(); + + // our node supports the current fork + assert_eq!(updater.state.lock().capability, CapState::Capable); + + // lets announce a new fork which our node doesn't support + latest.fork = 2; + operations_client.set_result(Some(latest.clone()), None); + updater.poll(); + + // our node is only capable of operating until block #2 when the fork triggers + assert_eq!(updater.state.lock().capability, CapState::CapableUntil(2)); + + client.add_blocks(3, EachBlockWith::Nothing); + updater.poll(); + + // after we move past the fork the capability should be updated to incapable + assert_eq!(updater.state.lock().capability, CapState::IncapableSince(2)); + + // and since our update policy requires consensus, the client should be disabled + assert!(client.is_disabled()); } } diff --git a/util/bloom/src/lib.rs b/util/bloom/src/lib.rs index 22a2cbc2aabb74d4b520c88d5bc6540035f438f3..32aad24bf26e44a9c7a700da0e8d5c8d588ab7ea 100644 --- a/util/bloom/src/lib.rs +++ b/util/bloom/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -14,7 +14,6 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . - extern crate siphasher; use std::cmp; @@ -208,7 +207,6 @@ pub struct BloomJournal { pub entries: Vec<(usize, u64)>, } - #[cfg(test)] mod tests { use super::Bloom; diff --git a/util/bloomchain/Cargo.toml b/util/bloomchain/Cargo.toml index a9bd8432c45cbd1f7a8b29e761b2769a7d59c6e9..6bcf6a9b1465e0a8a4732a6ac0e01ebc5682d1ad 100644 --- a/util/bloomchain/Cargo.toml +++ b/util/bloomchain/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT" keywords = ["ethereum", "ethcore", "bloom", "chain", "filter"] [dependencies] -ethbloom = "0.4" +ethbloom = "0.5" [dev-dependencies] rustc-hex = "1.0" diff --git a/util/bloomchain/src/chain.rs b/util/bloomchain/src/chain.rs index ba7bc21b356aeaef02711ce5dbc8d821fc6a6858..1017c874e4c63ef764f40fe0d4f2109e6559fcfa 100644 --- a/util/bloomchain/src/chain.rs +++ b/util/bloomchain/src/chain.rs @@ -1,3 +1,19 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + use std::collections::{HashMap, HashSet}; use std::ops::Range; use number::Number; diff --git a/util/bloomchain/src/config.rs b/util/bloomchain/src/config.rs index 3e729922a17962015a8b89d42377d17ad61b01be..58a600e1a1fe75af1d820e79595152f0a2182b7e 100644 --- a/util/bloomchain/src/config.rs +++ b/util/bloomchain/src/config.rs @@ -1,3 +1,19 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + /// `BloomChain` configuration. #[derive(Debug, PartialEq, Clone, Copy)] pub struct Config { diff --git a/util/bloomchain/src/database.rs b/util/bloomchain/src/database.rs index 9aba41e7c6427c977340986775263e66fbb4fd10..b6dc77a199e27cf89e3952186d62d6d039afab9b 100644 --- a/util/bloomchain/src/database.rs +++ b/util/bloomchain/src/database.rs @@ -1,3 +1,19 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + use position::Position; use bloom::Bloom; diff --git a/util/bloomchain/src/filter.rs b/util/bloomchain/src/filter.rs index 06d657ba4427483c5d91c5eb1a82f5068ee6e7df..83edd95a7248808ea486a889de4433c6c5d0629f 100644 --- a/util/bloomchain/src/filter.rs +++ b/util/bloomchain/src/filter.rs @@ -1,3 +1,19 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + use std::ops::Range; use bloom::Bloom; use number::Number; diff --git a/util/bloomchain/src/group/bridge.rs b/util/bloomchain/src/group/bridge.rs index b01650157c6326755b9df6d6f7baf1a9964d3e54..4efbec627461fb6203348007351e581012bf917d 100644 --- a/util/bloomchain/src/group/bridge.rs +++ b/util/bloomchain/src/group/bridge.rs @@ -1,3 +1,19 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + use bloom::Bloom; use config::Config; use database::BloomDatabase; diff --git a/util/bloomchain/src/group/chain.rs b/util/bloomchain/src/group/chain.rs index cfd7796f4d08d6fff46d708dbd478ea9b0ff5d57..3108ba649b9b7b1cf989c14933e3787bd20b55c7 100644 --- a/util/bloomchain/src/group/chain.rs +++ b/util/bloomchain/src/group/chain.rs @@ -1,3 +1,19 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + use std::collections::HashMap; use std::ops::Range; use bloom::Bloom; diff --git a/util/bloomchain/src/group/database.rs b/util/bloomchain/src/group/database.rs index 494184f3ebcff6d80616b0f6059840f091946249..a3d0847b6586ce9a45ad7ba12a3ac6937a293298 100644 --- a/util/bloomchain/src/group/database.rs +++ b/util/bloomchain/src/group/database.rs @@ -1,3 +1,19 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + use group::{GroupPosition, BloomGroup}; /// Readonly `BloomGroup` database. diff --git a/util/bloomchain/src/group/group.rs b/util/bloomchain/src/group/group.rs index 084c8f8e48e044d99f4919178af42d0cb6cb6deb..dc19926c587dd02847e3680ad9f10657d7ec89b6 100644 --- a/util/bloomchain/src/group/group.rs +++ b/util/bloomchain/src/group/group.rs @@ -1,3 +1,19 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + use bloom::Bloom; /// Group of blooms that are in the same index. diff --git a/util/bloomchain/src/group/mod.rs b/util/bloomchain/src/group/mod.rs index b6cabf628fca25b61594e1bf35371140dd452f21..9123037ec6a303513f6afd4087e75fa8ed7fba90 100644 --- a/util/bloomchain/src/group/mod.rs +++ b/util/bloomchain/src/group/mod.rs @@ -1,3 +1,19 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + //! Bloom grouping. //! //! Optimization gathering together blooms that are in the same index and are likely to be retrived together. diff --git a/util/bloomchain/src/group/position/manager.rs b/util/bloomchain/src/group/position/manager.rs index 611a5bb784c32c0929ff128b71b8a4ab8fa2a1a5..fc5656537a35df3d6b6b421712bb7ce4ece16a6d 100644 --- a/util/bloomchain/src/group/position/manager.rs +++ b/util/bloomchain/src/group/position/manager.rs @@ -1,3 +1,19 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + use super::{Position, GroupPosition}; use position::Position as BloomPosition; diff --git a/util/bloomchain/src/group/position/mod.rs b/util/bloomchain/src/group/position/mod.rs index fc95de4dd057bf01b783809f33f3b181e14ace3f..7173d1d9bb322d8cde82d48bead3ceae870e1b3d 100644 --- a/util/bloomchain/src/group/position/mod.rs +++ b/util/bloomchain/src/group/position/mod.rs @@ -1,3 +1,19 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + mod position; mod manager; diff --git a/util/bloomchain/src/group/position/position.rs b/util/bloomchain/src/group/position/position.rs index 88f26d69abaa76928d01309ad65b4ed0740c9847..1d8f89af543fc3e3c3832f40836fba1112b3b020 100644 --- a/util/bloomchain/src/group/position/position.rs +++ b/util/bloomchain/src/group/position/position.rs @@ -1,3 +1,19 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + /// Uniquely identifies bloom group position. #[derive(Debug, PartialEq, Eq, Hash, Clone)] pub struct GroupPosition { diff --git a/util/bloomchain/src/lib.rs b/util/bloomchain/src/lib.rs index 997ae083912ed3442a0bfb39cd874e11d1ad1a1e..a82b898caf73a4ca62306450bbbb112718004490 100644 --- a/util/bloomchain/src/lib.rs +++ b/util/bloomchain/src/lib.rs @@ -1,3 +1,19 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + extern crate ethbloom as bloom; mod chain; diff --git a/util/bloomchain/src/number.rs b/util/bloomchain/src/number.rs index 3ff82f195731e52fea673d65a29c13461929ff08..6c5af2e25dda5e9cd678235b0f91831be168fcd9 100644 --- a/util/bloomchain/src/number.rs +++ b/util/bloomchain/src/number.rs @@ -1,2 +1,18 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + /// Represents block number. pub type Number = usize; diff --git a/util/bloomchain/src/position/manager.rs b/util/bloomchain/src/position/manager.rs index a405878ab52eeb3692e002880a132bac7306444e..707afb667cf18f8e7c116505af5bedb76e462ad7 100644 --- a/util/bloomchain/src/position/manager.rs +++ b/util/bloomchain/src/position/manager.rs @@ -1,3 +1,19 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + //! Simplifies working with bloom indexes. use super::Position; diff --git a/util/bloomchain/src/position/mod.rs b/util/bloomchain/src/position/mod.rs index 4fa736a1636bd772393453b124bc20f12b2c8b03..623e9784e3cbe7b835b8d826bf3c2648d7838b1f 100644 --- a/util/bloomchain/src/position/mod.rs +++ b/util/bloomchain/src/position/mod.rs @@ -1,3 +1,19 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + pub mod position; pub mod manager; diff --git a/util/bloomchain/src/position/position.rs b/util/bloomchain/src/position/position.rs index 32845cbcc5db591a8098d5755f6c16cea58d40ea..c822d03e00b96f35b633a9e16b31e28c1e3355cd 100644 --- a/util/bloomchain/src/position/position.rs +++ b/util/bloomchain/src/position/position.rs @@ -1,3 +1,19 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + /// Uniquely identifies bloom position. #[derive(Debug, PartialEq, Eq, Hash)] pub struct Position { diff --git a/util/bloomchain/tests/bloomchain.rs b/util/bloomchain/tests/bloomchain.rs index 4a77407a7a2b3eee642c27f2c28c00457abc2d6c..f1e260bfdd3802c36aeb75f65b06eb2ed0c0aab3 100644 --- a/util/bloomchain/tests/bloomchain.rs +++ b/util/bloomchain/tests/bloomchain.rs @@ -1,3 +1,19 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + extern crate bloomchain; extern crate rustc_hex; @@ -53,7 +69,6 @@ fn partly_matching_bloom_searach() { db.insert_blooms(modified_blooms_1); - let chain = BloomChain::new(config, &db); assert_eq!(chain.with_bloom(&(0..100), &bloom2), vec![14, 15]); } @@ -101,7 +116,6 @@ fn bloom_replace() { db.insert_blooms(modified_blooms_3); - let reset_modified_blooms = { let chain = BloomChain::new(config, &db); chain.replace(&(15..17), vec![bloom4.clone(), bloom5.clone()]) diff --git a/util/bloomchain/tests/groupchain.rs b/util/bloomchain/tests/groupchain.rs index ec396346ac30ebf5d80da2c40a5e89cde4517707..048edc03ce3c4489f882e32f5d4b879fc24a9562 100644 --- a/util/bloomchain/tests/groupchain.rs +++ b/util/bloomchain/tests/groupchain.rs @@ -1,3 +1,19 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + extern crate bloomchain; extern crate rustc_hex; @@ -23,7 +39,6 @@ fn simple_bloom_group_search() { assert_eq!(modified_blooms.len(), config.levels); db.insert_blooms(modified_blooms); - let chain = BloomGroupChain::new(config, &db); assert_eq!(chain.with_bloom(&(0..100), &bloom), vec![23]); assert_eq!(chain.with_bloom(&(0..22), &bloom), vec![]); @@ -55,7 +70,6 @@ fn partly_matching_bloom_group_searach() { db.insert_blooms(modified_blooms_1); - let chain = BloomGroupChain::new(config, &db); assert_eq!(chain.with_bloom(&(0..100), &bloom2), vec![14, 15]); } @@ -103,7 +117,6 @@ fn bloom_group_replace() { db.insert_blooms(modified_blooms_3); - let reset_modified_blooms = { let chain = BloomGroupChain::new(config, &db); chain.replace(&(15..17), vec![bloom4.clone(), bloom5.clone()]) diff --git a/util/bloomchain/tests/util/db.rs b/util/bloomchain/tests/util/db.rs index 8101b37848d41e2af70608390e22088966feac27..b28e7b524b560d24bdcb2af4387fb702178a5838 100644 --- a/util/bloomchain/tests/util/db.rs +++ b/util/bloomchain/tests/util/db.rs @@ -1,3 +1,19 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + use std::collections::HashMap; use bloomchain::{Position, Bloom, BloomDatabase}; use bloomchain::group::{GroupPosition, BloomGroup, BloomGroupDatabase}; diff --git a/util/bloomchain/tests/util/each.rs b/util/bloomchain/tests/util/each.rs index 19ca1b67cf5a6a936e7b1329f5559d8cf8321c2f..1d8fc9a1d4b54452394a6764792647098d8f34db 100644 --- a/util/bloomchain/tests/util/each.rs +++ b/util/bloomchain/tests/util/each.rs @@ -1,3 +1,19 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + use std::io::{BufReader, Read, BufRead}; use bloomchain::Bloom; use super::FromHex; diff --git a/util/bloomchain/tests/util/from_hex.rs b/util/bloomchain/tests/util/from_hex.rs index 9152d304fdf33e508345009e2b6c99c227e4e37b..20c59333aee42b6ef06e13f2ab416b4e53ded850 100644 --- a/util/bloomchain/tests/util/from_hex.rs +++ b/util/bloomchain/tests/util/from_hex.rs @@ -1,3 +1,19 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + use rustc_hex::FromHex as RustcFromHex; use bloomchain::Bloom; diff --git a/util/bloomchain/tests/util/mod.rs b/util/bloomchain/tests/util/mod.rs index 2a1e55af9a0a27ffd722bbf1880695450d1c11f3..998e7c95206a48f1114f3d3a2f57840c6a39eac1 100644 --- a/util/bloomchain/tests/util/mod.rs +++ b/util/bloomchain/tests/util/mod.rs @@ -1,3 +1,19 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + mod db; mod each; mod from_hex; diff --git a/util/bloomchain/tests/util/random.rs b/util/bloomchain/tests/util/random.rs index 3d50b5ac10a8516af31d1ef29d9a69b9f1bcb064..06e3d1352088996bbd7b4f21488b133017e901c1 100644 --- a/util/bloomchain/tests/util/random.rs +++ b/util/bloomchain/tests/util/random.rs @@ -1,3 +1,19 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + extern crate rand; use self::rand::random; diff --git a/util/bytes/src/lib.rs b/util/bytes/src/lib.rs index 4303f7015012a71f5afcc4d53a3b613d83251e74..03b474559894e26d58a1a62337309e870742c4e5 100644 --- a/util/bytes/src/lib.rs +++ b/util/bytes/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/util/dir/Cargo.toml b/util/dir/Cargo.toml index c94086386c908eff499731f34c3c8e362184b680..e1a13401a25ee855d41e6ba37d28d20460c4c1e9 100644 --- a/util/dir/Cargo.toml +++ b/util/dir/Cargo.toml @@ -4,6 +4,6 @@ version = "0.1.0" authors = ["Parity Technologies "] [dependencies] -ethereum-types = "0.2" +ethereum-types = "0.3" journaldb = { path = "../journaldb" } -app_dirs = "1.1.1" +app_dirs = { git = "https://github.com/paritytech/app-dirs-rs" } diff --git a/util/dir/src/helpers.rs b/util/dir/src/helpers.rs index 95f8090c87834c8e8a78ee8947b1cdffaef50a2c..820b9dc5af65db3392fcc02db0141ed3f67a05ba 100644 --- a/util/dir/src/helpers.rs +++ b/util/dir/src/helpers.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/util/dir/src/lib.rs b/util/dir/src/lib.rs index 39175d000f3d2d3526239f24b77d03ca43164e43..7404a2cbcabdae60af3236e0debfafe69afe0139 100644 --- a/util/dir/src/lib.rs +++ b/util/dir/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -104,9 +104,9 @@ impl Directories { DatabaseDirectories { path: self.db.clone(), legacy_path: self.base.clone(), - genesis_hash: genesis_hash, - fork_name: fork_name, - spec_name: spec_name, + genesis_hash, + fork_name, + spec_name, } } @@ -156,7 +156,7 @@ impl DatabaseDirectories { /// Base DB directory for the given fork. // TODO: remove in 1.7 pub fn legacy_fork_path(&self) -> PathBuf { - Path::new(&self.legacy_path).join(format!("{:?}{}", H64::from(self.genesis_hash), self.fork_name.as_ref().map(|f| format!("-{}", f)).unwrap_or_default())) + Path::new(&self.legacy_path).join(format!("{:x}{}", H64::from(self.genesis_hash), self.fork_name.as_ref().map(|f| format!("-{}", f)).unwrap_or_default())) } /// Spec root directory for the given fork. @@ -232,9 +232,9 @@ pub fn default_local_path() -> String { } /// Default hypervisor path -pub fn default_hypervisor_path() -> String { +pub fn default_hypervisor_path() -> PathBuf { let app_info = AppInfo { name: PRODUCT_HYPERVISOR, author: AUTHOR }; - get_app_root(AppDataType::UserData, &app_info).map(|p| p.to_string_lossy().into_owned()).unwrap_or_else(|_| "$HOME/.parity-hypervisor".to_owned()) + get_app_root(AppDataType::UserData, &app_info).unwrap_or_else(|_| "$HOME/.parity-hypervisor".into()) } /// Get home directory. diff --git a/util/error/Cargo.toml b/util/error/Cargo.toml index ffbd8e4a7cd7beecd1bae971f746d464fd8b0843..d9da3e5c516f93bc694e9f8b088edd3471446541 100644 --- a/util/error/Cargo.toml +++ b/util/error/Cargo.toml @@ -6,6 +6,6 @@ authors = ["Parity Technologies "] [dependencies] rlp = { path = "../rlp" } kvdb = { path = "../kvdb" } -ethereum-types = "0.2" +ethereum-types = "0.3" error-chain = { version = "0.11", default-features = false } rustc-hex = "1.0" diff --git a/util/error/src/lib.rs b/util/error/src/lib.rs index 9a1ab875364f27d654e70e67342ceb888a07a386..bacc66c283e4ba8a5fe0df4d04c6162c4e5dfac5 100644 --- a/util/error/src/lib.rs +++ b/util/error/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -74,4 +74,3 @@ error_chain! { BaseData(BaseDataError); } } - diff --git a/util/fake-fetch/Cargo.toml b/util/fake-fetch/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..217d7f461a5b30dd658c5903489c33581ea38972 --- /dev/null +++ b/util/fake-fetch/Cargo.toml @@ -0,0 +1,11 @@ +[package] +description = "Mock fetcher for testing" +name = "fake-fetch" +version = "0.0.1" +license = "GPL-3.0" +authors = ["Parity Technologies "] + +[dependencies] +fetch = { path = "../fetch" } +futures = "0.1" +hyper = "0.11" diff --git a/util/fake-fetch/src/lib.rs b/util/fake-fetch/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..9dbe05b9cab2e9fa079634d1c446946a07aa25ae --- /dev/null +++ b/util/fake-fetch/src/lib.rs @@ -0,0 +1,64 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +extern crate fetch; +extern crate hyper; +extern crate futures; + +use hyper::StatusCode; +use futures::{future, future::FutureResult}; +use fetch::{Fetch, Url, Request}; + +#[derive(Clone, Default)] +pub struct FakeFetch where T: Clone + Send + Sync { + val: Option, +} + +impl FakeFetch where T: Clone + Send + Sync { + pub fn new(t: Option) -> Self { + FakeFetch { val : t } + } +} + +impl Fetch for FakeFetch where T: Clone + Send+ Sync { + type Result = FutureResult; + + fn fetch(&self, request: Request, abort: fetch::Abort) -> Self::Result { + let u = request.url().clone(); + future::ok(if self.val.is_some() { + let r = hyper::Response::new().with_body(&b"Some content"[..]); + fetch::client::Response::new(u, r, abort) + } else { + fetch::client::Response::new(u, hyper::Response::new().with_status(StatusCode::NotFound), abort) + }) + } + + fn get(&self, url: &str, abort: fetch::Abort) -> Self::Result { + let url: Url = match url.parse() { + Ok(u) => u, + Err(e) => return future::err(e.into()) + }; + self.fetch(Request::get(url), abort) + } + + fn post(&self, url: &str, abort: fetch::Abort) -> Self::Result { + let url: Url = match url.parse() { + Ok(u) => u, + Err(e) => return future::err(e.into()) + }; + self.fetch(Request::post(url), abort) + } +} diff --git a/util/fetch/Cargo.toml b/util/fetch/Cargo.toml index 61925e189d1d107046408694d15b3bc22bfaee58..98b1fc58234bb096a2f54b011bc28a65a44bb843 100644 --- a/util/fetch/Cargo.toml +++ b/util/fetch/Cargo.toml @@ -14,6 +14,7 @@ hyper-rustls = "0.11" log = "0.4" tokio-core = "0.1" url = "1" +bytes = "0.4" [features] default = [] diff --git a/util/fetch/src/client.rs b/util/fetch/src/client.rs index c57a362ca95b1af6c668b22ca1a00055924e4721..cda802cfb0c927e70be8ed7bd369aaacaa3e1c0e 100644 --- a/util/fetch/src/client.rs +++ b/util/fetch/src/client.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ use futures::{self, Future, Async, Sink, Stream}; use futures_timer::FutureExt; use hyper::header::{UserAgent, Location, ContentLength, ContentType}; use hyper::mime::Mime; -use hyper::{self, Request, Method, StatusCode}; +use hyper::{self, Method, StatusCode}; use hyper_rustls; use std; use std::cmp::min; @@ -32,9 +32,10 @@ use std::time::Duration; use std::{io, fmt}; use tokio_core::reactor; use url::{self, Url}; +use bytes::Bytes; const MAX_SIZE: usize = 64 * 1024 * 1024; -const MAX_SECS: u64 = 5; +const MAX_SECS: Duration = Duration::from_secs(5); const MAX_REDR: usize = 5; /// A handle to abort requests. @@ -55,7 +56,7 @@ impl Default for Abort { Abort { abort: Arc::new(AtomicBool::new(false)), size: MAX_SIZE, - time: Duration::from_secs(MAX_SECS), + time: MAX_SECS, redir: MAX_REDR, } } @@ -66,7 +67,7 @@ impl From> for Abort { Abort { abort: a, size: MAX_SIZE, - time: Duration::from_secs(MAX_SECS), + time: MAX_SECS, redir: MAX_REDR, } } @@ -119,13 +120,19 @@ pub trait Fetch: Clone + Send + Sync + 'static { /// The result future. type Result: Future + Send + 'static; + /// Make a request to given URL + fn fetch(&self, request: Request, abort: Abort) -> Self::Result; + /// Get content from some URL. - fn fetch(&self, url: &str, abort: Abort) -> Self::Result; + fn get(&self, url: &str, abort: Abort) -> Self::Result; + + /// Post content to some URL. + fn post(&self, url: &str, abort: Abort) -> Self::Result; } type TxResponse = oneshot::Sender>; type TxStartup = std::sync::mpsc::SyncSender>; -type ChanItem = Option<(Url, Abort, TxResponse)>; +type ChanItem = Option<(Request, Abort, TxResponse)>; /// An implementation of `Fetch` using a `hyper` client. // Due to the `Send` bound of `Fetch` we spawn a background thread for @@ -203,29 +210,37 @@ impl Client { let future = rx_proto.take_while(|item| Ok(item.is_some())) .map(|item| item.expect("`take_while` is only passing on channel items != None; qed")) - .for_each(|(url, abort, sender)| + .for_each(|(request, abort, sender)| { - trace!(target: "fetch", "new request to {}", url); + trace!(target: "fetch", "new request to {}", request.url()); if abort.is_aborted() { return future::ok(sender.send(Err(Error::Aborted)).unwrap_or(())) } - let ini = (hyper.clone(), url, abort, 0); - let fut = future::loop_fn(ini, |(client, url, abort, redirects)| { - let url2 = url.clone(); + let ini = (hyper.clone(), request, abort, 0); + let fut = future::loop_fn(ini, |(client, request, abort, redirects)| { + let request2 = request.clone(); + let url2 = request2.url().clone(); let abort2 = abort.clone(); - client.request(get(&url)) + client.request(request.into()) .map(move |resp| Response::new(url2, resp, abort2)) .from_err() .and_then(move |resp| { if abort.is_aborted() { - debug!(target: "fetch", "fetch of {} aborted", url); + debug!(target: "fetch", "fetch of {} aborted", request2.url()); return Err(Error::Aborted) } - if let Some(next_url) = redirect_location(url, &resp) { + if let Some((next_url, preserve_method)) = redirect_location(request2.url().clone(), &resp) { if redirects >= abort.max_redirects() { return Err(Error::TooManyRedirects) } - Ok(Loop::Continue((client, next_url, abort, redirects + 1))) + let request = if preserve_method { + let mut request2 = request2.clone(); + request2.set_url(next_url); + request2 + } else { + Request::new(next_url, Method::Get) + }; + Ok(Loop::Continue((client, request, abort, redirects + 1))) } else { let content_len = resp.headers.get::().cloned(); if content_len.map(|n| *n > abort.max_size() as u64).unwrap_or(false) { @@ -257,19 +272,15 @@ impl Client { impl Fetch for Client { type Result = Box + Send>; - fn fetch(&self, url: &str, abort: Abort) -> Self::Result { - debug!(target: "fetch", "fetching: {:?}", url); + fn fetch(&self, request: Request, abort: Abort) -> Self::Result { + debug!(target: "fetch", "fetching: {:?}", request.url()); if abort.is_aborted() { return Box::new(future::err(Error::Aborted)) } - let url: Url = match url.parse() { - Ok(u) => u, - Err(e) => return Box::new(future::err(e.into())) - }; let (tx_res, rx_res) = oneshot::channel(); let maxdur = abort.max_duration(); let sender = self.core.clone(); - let future = sender.send(Some((url.clone(), abort, tx_res))) + let future = sender.send(Some((request, abort, tx_res))) .map_err(|e| { error!(target: "fetch", "failed to schedule request: {}", e); Error::BackgroundThreadDead @@ -287,11 +298,33 @@ impl Fetch for Client { }); Box::new(future) } + + /// Get content from some URL. + fn get(&self, url: &str, abort: Abort) -> Self::Result { + let url: Url = match url.parse() { + Ok(u) => u, + Err(e) => return Box::new(future::err(e.into())) + }; + self.fetch(Request::get(url), abort) + } + + /// Post content to some URL. + fn post(&self, url: &str, abort: Abort) -> Self::Result { + let url: Url = match url.parse() { + Ok(u) => u, + Err(e) => return Box::new(future::err(e.into())) + }; + self.fetch(Request::post(url), abort) + } } -// Extract redirect location from response. -fn redirect_location(u: Url, r: &Response) -> Option { +// Extract redirect location from response. The second return value indicate whether the original method should be preserved. +fn redirect_location(u: Url, r: &Response) -> Option<(Url, bool)> { use hyper::StatusCode::*; + let preserve_method = match r.status() { + TemporaryRedirect | PermanentRedirect => true, + _ => false, + }; match r.status() { MovedPermanently | PermanentRedirect @@ -299,7 +332,7 @@ fn redirect_location(u: Url, r: &Response) -> Option { | Found | SeeOther => { if let Some(loc) = r.headers.get::() { - u.join(loc).ok() + u.join(loc).ok().map(|url| (url, preserve_method)) } else { None } @@ -308,12 +341,84 @@ fn redirect_location(u: Url, r: &Response) -> Option { } } -// Build a simple GET request for the given Url. -fn get(u: &Url) -> hyper::Request { - let uri = u.as_ref().parse().expect("Every valid URL is aso a URI."); - let mut rq = Request::new(Method::Get, uri); - rq.headers_mut().set(UserAgent::new("Parity Fetch Neo")); - rq +/// A wrapper for hyper::Request using Url and with methods. +#[derive(Debug, Clone)] +pub struct Request { + url: Url, + method: Method, + headers: hyper::Headers, + body: Bytes, +} + +impl Request { + /// Create a new request, with given url and method. + pub fn new(url: Url, method: Method) -> Request { + Request { + url, method, + headers: hyper::Headers::new(), + body: Default::default(), + } + } + + /// Create a new GET request. + pub fn get(url: Url) -> Request { + Request::new(url, Method::Get) + } + + /// Create a new empty POST request. + pub fn post(url: Url) -> Request { + Request::new(url, Method::Post) + } + + /// Read the url. + pub fn url(&self) -> &Url { + &self.url + } + + /// Read the request headers. + pub fn headers(&self) -> &hyper::Headers { + &self.headers + } + + /// Get a mutable reference to the headers. + pub fn headers_mut(&mut self) -> &mut hyper::Headers { + &mut self.headers + } + + /// Set the body of the request. + pub fn set_body>(&mut self, body: T) { + self.body = body.into(); + } + + /// Set the url of the request. + pub fn set_url(&mut self, url: Url) { + self.url = url; + } + + /// Consume self, and return it with the added given header. + pub fn with_header(mut self, value: H) -> Self { + self.headers_mut().set(value); + self + } + + /// Consume self, and return it with the body. + pub fn with_body>(mut self, body: T) -> Self { + self.set_body(body); + self + } +} + +impl Into for Request { + fn into(mut self) -> hyper::Request { + let uri = self.url.as_ref().parse().expect("Every valid URLis also a URI."); + let mut req = hyper::Request::new(self.method, uri); + + self.headers.set(UserAgent::new("Parity Fetch Neo")); + *req.headers_mut() = self.headers; + req.set_body(self.body); + + req + } } /// An HTTP response. @@ -350,6 +455,11 @@ impl Response { self.status() == StatusCode::Ok } + /// Status code == 404. + pub fn is_not_found(&self) -> bool { + self.status() == StatusCode::NotFound + } + /// Is the content-type text/html? pub fn is_html(&self) -> bool { if let Some(ref mime) = self.content_type() { @@ -524,7 +634,7 @@ mod test { fn it_should_fetch() { let server = TestServer::run(); let client = Client::new().unwrap(); - let future = client.fetch(&format!("http://{}?123", server.addr()), Default::default()); + let future = client.get(&format!("http://{}?123", server.addr()), Default::default()); let resp = future.wait().unwrap(); assert!(resp.is_success()); let body = resp.concat2().wait().unwrap(); @@ -536,7 +646,7 @@ mod test { let server = TestServer::run(); let client = Client::new().unwrap(); let abort = Abort::default().with_max_duration(Duration::from_secs(1)); - match client.fetch(&format!("http://{}/delay?3", server.addr()), abort).wait() { + match client.get(&format!("http://{}/delay?3", server.addr()), abort).wait() { Err(Error::Timeout) => {} other => panic!("expected timeout, got {:?}", other) } @@ -547,7 +657,7 @@ mod test { let server = TestServer::run(); let client = Client::new().unwrap(); let abort = Abort::default(); - let future = client.fetch(&format!("http://{}/redirect?http://{}/", server.addr(), server.addr()), abort); + let future = client.get(&format!("http://{}/redirect?http://{}/", server.addr(), server.addr()), abort); assert!(future.wait().unwrap().is_success()) } @@ -556,7 +666,7 @@ mod test { let server = TestServer::run(); let client = Client::new().unwrap(); let abort = Abort::default().with_max_redirects(4); - let future = client.fetch(&format!("http://{}/redirect?/", server.addr()), abort); + let future = client.get(&format!("http://{}/redirect?/", server.addr()), abort); assert!(future.wait().unwrap().is_success()) } @@ -565,7 +675,7 @@ mod test { let server = TestServer::run(); let client = Client::new().unwrap(); let abort = Abort::default().with_max_redirects(3); - match client.fetch(&format!("http://{}/loop", server.addr()), abort).wait() { + match client.get(&format!("http://{}/loop", server.addr()), abort).wait() { Err(Error::TooManyRedirects) => {} other => panic!("expected too many redirects error, got {:?}", other) } @@ -576,7 +686,7 @@ mod test { let server = TestServer::run(); let client = Client::new().unwrap(); let abort = Abort::default(); - let future = client.fetch(&format!("http://{}?abcdefghijklmnopqrstuvwxyz", server.addr()), abort); + let future = client.get(&format!("http://{}?abcdefghijklmnopqrstuvwxyz", server.addr()), abort); let resp = future.wait().unwrap(); assert!(resp.is_success()); assert_eq!(&resp.concat2().wait().unwrap()[..], b"abcdefghijklmnopqrstuvwxyz") @@ -587,7 +697,7 @@ mod test { let server = TestServer::run(); let client = Client::new().unwrap(); let abort = Abort::default().with_max_size(3); - let resp = client.fetch(&format!("http://{}/?1234", server.addr()), abort).wait().unwrap(); + let resp = client.get(&format!("http://{}/?1234", server.addr()), abort).wait().unwrap(); assert!(resp.is_success()); match resp.concat2().wait() { Err(Error::SizeLimit) => {} @@ -600,7 +710,7 @@ mod test { let server = TestServer::run(); let client = Client::new().unwrap(); let abort = Abort::default().with_max_size(3); - let resp = client.fetch(&format!("http://{}/?1234", server.addr()), abort).wait().unwrap(); + let resp = client.get(&format!("http://{}/?1234", server.addr()), abort).wait().unwrap(); assert!(resp.is_success()); let mut buffer = Vec::new(); let mut reader = BodyReader::new(resp); diff --git a/util/fetch/src/lib.rs b/util/fetch/src/lib.rs index a5722dfe26a925a0b24aecb7c0e319b9317f634a..8e50fa5e6a55da1d1f44a8969d88334607a28dc9 100644 --- a/util/fetch/src/lib.rs +++ b/util/fetch/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -30,10 +30,11 @@ extern crate hyper_rustls; extern crate tokio_core; extern crate url; +extern crate bytes; /// Fetch client implementation. pub mod client; pub use url::Url; -pub use self::client::{Client, Fetch, Error, Response, Abort, BodyReader}; - +pub use self::client::{Client, Fetch, Error, Response, Request, Abort, BodyReader}; +pub use hyper::Method; diff --git a/util/hash/Cargo.toml b/util/hash/Cargo.toml index c7fee36e016f82ada58f87fa444f197325004b8a..4ca503751aa9bd73c8d4a4a66d3741f296c2a032 100644 --- a/util/hash/Cargo.toml +++ b/util/hash/Cargo.toml @@ -1,18 +1,15 @@ [package] description = "Rust bindings for tinykeccak C library" -homepage = "http://parity.io" +homepage = "https://github.com/paritytech/keccak-hash" +readme = "README.md" license = "GPL-3.0" name = "keccak-hash" -version = "0.1.0" +version = "0.1.2" authors = ["Parity Technologies "] -build = "build.rs" [dependencies] -ethereum-types = "0.2" -tiny-keccak = "1.3" - -[build-dependencies] -cc = "1.0" +ethereum-types = "0.3" +tiny-keccak = "1.4" [dev-dependencies] tempdir = "0.3" diff --git a/util/hash/benches/keccak_256.rs b/util/hash/benches/keccak_256.rs new file mode 100644 index 0000000000000000000000000000000000000000..d59e534104f26a36865c45d8aa97c2c86c9d207d --- /dev/null +++ b/util/hash/benches/keccak_256.rs @@ -0,0 +1,52 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +#![feature(test)] + +extern crate test; +extern crate ethereum_types; +extern crate keccak_hash; + +use keccak_hash::{keccak, write_keccak}; +use test::Bencher; + +#[bench] +fn bench_keccak_256_with_empty_input(b: &mut Bencher) { + let empty = [0u8;0]; + b.bytes = empty.len() as u64; + b.iter(|| { + let _out = keccak(empty); + }) +} + +#[bench] +fn bench_keccak_256_with_typical_input(b: &mut Bencher) { + let data: Vec = From::from("some medum length string with important information"); + b.bytes = data.len() as u64; + b.iter(|| { + let _out = keccak(&data); + }) +} + +#[bench] +fn bench_keccak_256_with_large_input(b: &mut Bencher) { + // 4096 chars + let data: Vec = From::from("IGxcKBr1Qp7tuqtpSVhAbvt7UgWLEi7mCA6Wa185seLSIJLFS8K1aAFO9AwtO9b3n9SM3Qg136JMmy9Mj9gZ84IaUm8XioPtloabFDU5ZR1wvauJT6jNTkvBVBpUigIsyU7C1u3s99vKP64LpXqvo1hwItZKtISxmUAgzzjv5q14V4G9bkKAnmc4M5xixgLsDGZmnj6HcOMY3XRkWtxN3RscSKwPA0bfpgtz27ZVHplbXwloYRgRLpjRhZJc7sqO8RFnTHKasVkxVRcUoDBvWNJK27TbLvQQcfxETI2Q1H6c2cBAchi8unSiuxqy5rIvVxcl9rsmmRY4IXLEG9qKntUGbiIRLjEffIP9ODoWog0GbWLmMtfvtf24hWVwXz6Ap5oUAR0kLgb7HYIYrOwKjvfV25iEF7GW8cjhl8yowXx1zcgW4t6NJNqJlGzRKx8MvRWQXvHz8h8JxcHl7S64i6PAkxI9eCLXLvs8cpbEQQHt05Zu6GKm6IInjc9mSh52WFuGhgjbno69XzfkBufJs6c9tZuBf6ErVPj4UxmT82ajCruDusk79Tlvb8oQMLjoplQc1alQaLQwSsMac9iVp9MiE3PeYnTTepJ1V10tp79fciDAnNPJgPcRfDYv0REcSFgR9Q7yWhbpPpyBjO7HwOykDQVGtV0ZbDFrFRygLAXagAIkOPc9HDfcBNID1Q2MGk8ijVWMyvmGz1wzbpNfFcQaSOm8olhwoLyHUGvkyXegh44iNsPBUvSicNxTTDowtMqO5azleuWEjzxCobYbASDopvl6JeJjRtEBBO5YCQJiHsYjlXh9QR5Q543GsqhzRLgcHNRSZYLMZqDmIABXZi8VRNJMZyWXDRKHOGDmcHWe55uZomW6FnyU0uSRKxxz66K0JWfxuFzzxAR0vR4ZZCTemgDRQuDwL1loC3KUMjDpU13jUgoPc4UJUVfwQ4f4BUY3X51Cfw9FLw4oX39KoFoiCP2Z6z27gZUY1IlE59WoXGLj4KjTp4C16ZihG080gfDIWlXnDEk3VwBuBFyKWARB63sGLrGnn27b1gHWMaop6sPvkQgWxkEKIqsxDIvXLZJg2s23V8Gqtt0FeA7R3RCvBysF4jNjQ7NiQTIQWQZ8G9gO4mEsftolSZv6FlSpNeBKIIwYWSO2R6vkgeiz06euE9bwwnenOjwPNGTGk8WHIOZBJ1hIP0ejVU2i2ca9ON0phSAnewqjo5W3PtZf2Q7mDvp9imuVWoy4t8XcZq8I2Un9jVjes9Xi0FLN2t71vLFWLWZmGDzwXxpqEgkARS1WjtJoYXCBmRnXEPj6jQfwMZWKPYSIrmOogxMVoWvA8wrof6utfJna9JezyTnrBJSCuGTSNmwwAXRLoFYxF1RITyN8mI2KmHSfvLXBrbE6kmAkjsm4XJb6kria7oUQQ1gzJuCyB7oNHjZTBFNhNa7VeQ1s1xLOwZXLOAjZ4MDTYKnF7giGJGyswb5KQxkOV9orbuAu6pJsjtql6h1UD3BcNUkG3oz8kJNepbuCN3vNCJcZOX1VrQi0PWkDwyvECrQ2E1CgbU6GpWatpg2sCTpo9W62pCcWBK2FKUFWqU3qo2T7T1Mk2ZtM6hE9I8op0M7xlGE91Mn7ea6aq93MWp7nvFlBvbaMIoeU4MpDx0BeOSkROY03ZBJ0x7K8nJrNUhAtvxp17c9oFk0VxLiuRbAAcwDUormOmpVXZNIcqnap4twEVYaSIowfcNojyUSrFL5nPc8ZG93WgNNl9rpUPZhssVml3DvXghI80A9SW3QauzohTQAX2bkWelFBHnuG2LKrsJ8en51N6CkjcS5b87y1DVMZELcZ1n5s8PCAA1wyn7OSZlgw00GRzch1YwMoHzBBgIUtMO9HrMyuhgqIPJP7KcKbQkKhtvBXKplX8SCfSlOwUkLwHNKm3HYVE0uVfJ91NAsUrGoCOjYiXYpoRT8bjAPWTm6fDlTq2sbPOyTMoc4xRasmiOJ7B0PT6UxPzCPImM4100sPFxp7Kofv4okKZWTPKTefeYiPefI3jRgfDtEIP9E6a35LZD75lBNMXYlAqL3qlnheUQD1WQimFTHiDsW6bmURptNvtkMjEXzXzpWbnyxBskUGTvP2YQjtSAhWliDXkv6t1x71cYav7TQbqvbIzMRQQsguSGYMbs8YIC4DC9ep5reWAfanlTxcxksbEhQ7FGzXOvcufeGnDl2C85gWfryVzwN7kOZiSEktFMOQ1ngRC23y1fCOiHQVQJ2nLnaW7GILb9wkN1mBTRuHsOefRJST0TnRxcn4bBq4MIibIitVyjPRy7G5XvPEcL4pFaW1HCPGm6pUOEEwTer32JObNGCyTFB1BI2cRLJu5BHPjgG3mmb0gGkGlIfh8D2b2amogpivqEn2r9Y1KOKQ8ufJvG2mYfkevco9DuEZ9Nmzkm6XkCTZaFMNHqbfQaKqsEYK7i2N1KfkBct1leW2H9MQ9QO7AHCqXHK47b1kWVIm6pSJA1yV4funzCqXnIJCEURQgHiKf38YpN7ylLhe1J4UvSG3KeesZNeFFIZOEP9HZUSFMpnN1MOrwejojK0D4qzwucYWtXrTQ8I7UP5QhlijIsCKckUa9C1Osjrq8cgSclYNGt19wpy0onUbX1rOQBUlAAUJs4CyXNU0wmVUjw7tG1LUC8my4s9KZDUj4R5UcPz3VaZRrx1RqYu6YxjroJW70I1LyG4WEiQbOkCoLmaiWo9WzbUS2cErlOo2RPymlkWHxbNnZawX2Bc872ivRHSWqNpRHyuR5QewXmcyghH3EhESBAxTel5E2xuQXfLCEVK0kEk0Mj22KPsckKKyH7sVYC1F4YItQh5hj9Titb7KflQb9vnXQ44UHxY3zBhTQT5PSYv1Kv8HxXCsnpmhZCiBru16iX9oEB33icBVB2KKcZZEEKnCGPVxJlM9RTlyNyQmjHf7z4GeTDuMAUrsMO31WvgZBnWcAOtn6ulBTUCAaqxJiWqzlMx2FSANAlyAjAxqzmQjzPLvQRjskUnBFN3woKB1m2bSo2c5thwA1fKiPvN5LW8tl1rnfNy3rJ0GJpK8nZjkzHMztYrKYAe56pX4SvplpTyibTIiRXLyEVsmuByTHCZhO3fvGoFsav3ZuRhe9eAAWeqAh13eKDTcA0ufME3ZnmJheXEZ3OwrxnFjSf3U0clkWYVont3neh77ODKHhYnX0bOmnJJlr4RqFoLBitskY0kcGMKcZlaej21SENjDcFgaka3CfHbAH5vIFqnoX1JZrZPkQ65PZqQWImP79U3gXWKvz96lElyJZAFqn0Mbltllqw4MhlI766AvHraOmMsJoNvjv1QR7pCSnC0iX6nbqW1eVPaUSZDuZRtRIxfLA8HC9VbxufT2KZV3qG0l7wrZna5Di2MNcBE9uthuVLZcqp8vCmEhINDhRRlipR7tC2iRBHecS5WtxBCpbEm1y1kgNG5o60UKgAswxxuJ3RQ9Y49mPIApBMmp4LFpuKRfcrZb4UJnCfR3pNbQ70nnZ6Be2M7tuJUCoFfHrhqHXNz5A0uWMgxUS50c60zLl6QAELxHaCGba4WCMOHIo5nSKcUuYtDyDoDlrezALW5mZR4PRPRxnjrXxbJI14qrpymRReC3QgFDJp6sT5TLwvSHaavPlEbt2Eu0Kh5SXklGHXP9YuF3glGuJzSob3NakW1RXF5786U1MHhtJby64LyGWvNn4QXie3VjeL3QQu4C9crEAxSSiOJOfnL3DYIVOY4ipUkKFlF7Rp2q6gZazDvcUCp1cbcr7T7B4s22rXzjN7mHYWOyWuZGwlImeorY3aVKi7BaXbhgOFw6BUmIc1HeGFELHIEnPE9MwOjZam3LOm0rhBHlvJJZkXvJKmDUJrGlyqC5GtC5lDWLfXewyDWDqq7PY0atVQily5GWqib6wub6u6LZ3HZDNP8gK64Nf4kC259AE4V2hCohDnSsXAIoOkehwXyp6CkDT42NJb6sXHUv2N6cm292MiKA22PKWrwUGsan599KI2V67YRDfcfiB4ZHRDiSe62MBE0fGLIgXLIWw1xTWYbPQ9YAj3xovBvmewbJ1De4k6uS"); + b.bytes = data.len() as u64; + b.iter(|| { + let _out = keccak(&data); + }) +} diff --git a/util/hash/src/lib.rs b/util/hash/src/lib.rs index 0982c11da413c6e8d2df35540fffa6c8df50838b..c54d7233cdf6a03a4860093147b7af18ab8648e8 100644 --- a/util/hash/src/lib.rs +++ b/util/hash/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -18,7 +18,9 @@ extern crate ethereum_types; extern crate tiny_keccak; use std::io; +use std::slice; use tiny_keccak::Keccak; + pub use ethereum_types::H256; /// Get the KECCAK (i.e. Keccak) hash of the empty bytes string. @@ -30,29 +32,37 @@ pub const KECCAK_NULL_RLP: H256 = H256( [0x56, 0xe8, 0x1f, 0x17, 0x1b, 0xcc, 0x5 /// The KECCAK of the RLP encoding of empty list. pub const KECCAK_EMPTY_LIST_RLP: H256 = H256( [0x1d, 0xcc, 0x4d, 0xe8, 0xde, 0xc7, 0x5d, 0x7a, 0xab, 0x85, 0xb5, 0x67, 0xb6, 0xcc, 0xd4, 0x1a, 0xd3, 0x12, 0x45, 0x1b, 0x94, 0x8a, 0x74, 0x13, 0xf0, 0xa1, 0x42, 0xfd, 0x40, 0xd4, 0x93, 0x47] ); -extern { - /// Hashes input. Returns -1 if either out or input does not exist. Otherwise returns 0. - pub fn keccak_256(out: *mut u8, outlen: usize, input: *const u8, inputlen: usize) -> i32; - /// Hashes input. Returns -1 if either out or input does not exist. Otherwise returns 0. - pub fn keccak_512(out: *mut u8, outlen: usize, input: *const u8, inputlen: usize) -> i32; -} - pub fn keccak>(s: T) -> H256 { let mut result = [0u8; 32]; write_keccak(s, &mut result); H256(result) } -pub fn write_keccak>(s: T, dest: &mut [u8]) { - let input = s.as_ref(); - unsafe { - // we can safely ignore keccak_256 output, cause we know that both input - // and dest are properly allocated - keccak_256(dest.as_mut_ptr(), dest.len(), input.as_ptr(), input.len()); - } +pub unsafe fn keccak_256_unchecked(out: *mut u8, outlen: usize, input: *const u8, inputlen: usize) { + // This is safe since `keccak_*` uses an internal buffer and copies the result to the output. This + // means that we can reuse the input buffer for both input and output. + Keccak::keccak256( + slice::from_raw_parts(input, inputlen), + slice::from_raw_parts_mut(out, outlen) + ); } -pub fn keccak_buffer(r: &mut io::BufRead) -> Result { +pub unsafe fn keccak_512_unchecked(out: *mut u8, outlen: usize, input: *const u8, inputlen: usize) { + // This is safe since `keccak_*` uses an internal buffer and copies the result to the output. This + // means that we can reuse the input buffer for both input and output. + Keccak::keccak512( + slice::from_raw_parts(input, inputlen), + slice::from_raw_parts_mut(out, outlen) + ); +} + +pub fn keccak_256(input: &[u8], mut output: &mut [u8]) { Keccak::keccak256(input, &mut output); } + +pub fn keccak_512(input: &[u8], mut output: &mut [u8]) { Keccak::keccak512(input, &mut output); } + +pub fn write_keccak>(s: T, dest: &mut [u8]) { Keccak::keccak256(s.as_ref(), dest); } + +pub fn keccak_pipe(r: &mut io::BufRead, w: &mut io::Write) -> Result { let mut output = [0u8; 32]; let mut input = [0u8; 1024]; let mut keccak = Keccak::new_keccak256(); @@ -64,12 +74,17 @@ pub fn keccak_buffer(r: &mut io::BufRead) -> Result { break; } keccak.update(&input[0..some]); + w.write_all(&input[0..some])?; } keccak.finalize(&mut output); Ok(output.into()) } +pub fn keccak_buffer(r: &mut io::BufRead) -> Result { + keccak_pipe(r, &mut io::sink()) +} + #[cfg(test)] mod tests { extern crate tempdir; @@ -77,17 +92,33 @@ mod tests { use std::fs; use std::io::{Write, BufReader}; use self::tempdir::TempDir; - use super::{keccak, keccak_buffer, KECCAK_EMPTY}; + use super::{keccak, write_keccak, keccak_buffer, KECCAK_EMPTY}; #[test] fn keccak_empty() { assert_eq!(keccak([0u8; 0]), KECCAK_EMPTY); } + #[test] fn keccak_as() { assert_eq!(keccak([0x41u8; 32]), From::from("59cad5948673622c1d64e2322488bf01619f7ff45789741b15a9f782ce9290a8")); } + #[test] + fn write_keccak_with_content() { + let data: Vec = From::from("hello world"); + let expected = vec![ + 0x47, 0x17, 0x32, 0x85, 0xa8, 0xd7, 0x34, 0x1e, + 0x5e, 0x97, 0x2f, 0xc6, 0x77, 0x28, 0x63, 0x84, + 0xf8, 0x02, 0xf8, 0xef, 0x42, 0xa5, 0xec, 0x5f, + 0x03, 0xbb, 0xfa, 0x25, 0x4c, 0xb0, 0x1f, 0xad + ]; + let mut dest = [0u8;32]; + write_keccak(data, &mut dest); + + assert_eq!(dest, expected.as_ref()); + } + #[test] fn should_keccak_a_file() { // given @@ -105,6 +136,6 @@ mod tests { let hash = keccak_buffer(&mut file).unwrap(); // then - assert_eq!(format!("{:?}", hash), "68371d7e884c168ae2022c82bd837d51837718a7f7dfb7aa3f753074a35e1d87"); + assert_eq!(format!("{:x}", hash), "68371d7e884c168ae2022c82bd837d51837718a7f7dfb7aa3f753074a35e1d87"); } } diff --git a/util/hash/src/tinykeccak.c b/util/hash/src/tinykeccak.c deleted file mode 100644 index bfe172e5f6d5f90ac7d62564397588475fe3be84..0000000000000000000000000000000000000000 --- a/util/hash/src/tinykeccak.c +++ /dev/null @@ -1,177 +0,0 @@ -#include -#include - -/** libkeccak-tiny - * - * A single-file implementation of SHA-3 and SHAKE. - * - * Implementor: David Leon Gil - * License: CC0, attribution kindly requested. Blame taken too, - * but not liability. - */ - -#define decshake(bits) \ - int shake##bits(uint8_t*, size_t, const uint8_t*, size_t); - -#define deckeccak(bits) \ - int keccak_##bits(uint8_t*, size_t, const uint8_t*, size_t); - -decshake(128) -decshake(256) -deckeccak(224) -deckeccak(256) -deckeccak(384) -deckeccak(512) - -/******** The Keccak-f[1600] permutation ********/ - -/*** Constants. ***/ -static const uint8_t rho[24] = \ - { 1, 3, 6, 10, 15, 21, - 28, 36, 45, 55, 2, 14, - 27, 41, 56, 8, 25, 43, - 62, 18, 39, 61, 20, 44}; -static const uint8_t pi[24] = \ - {10, 7, 11, 17, 18, 3, - 5, 16, 8, 21, 24, 4, - 15, 23, 19, 13, 12, 2, - 20, 14, 22, 9, 6, 1}; -static const uint64_t RC[24] = \ - {1ULL, 0x8082ULL, 0x800000000000808aULL, 0x8000000080008000ULL, - 0x808bULL, 0x80000001ULL, 0x8000000080008081ULL, 0x8000000000008009ULL, - 0x8aULL, 0x88ULL, 0x80008009ULL, 0x8000000aULL, - 0x8000808bULL, 0x800000000000008bULL, 0x8000000000008089ULL, 0x8000000000008003ULL, - 0x8000000000008002ULL, 0x8000000000000080ULL, 0x800aULL, 0x800000008000000aULL, - 0x8000000080008081ULL, 0x8000000000008080ULL, 0x80000001ULL, 0x8000000080008008ULL}; - -/*** Helper macros to unroll the permutation. ***/ -#define rol(x, s) (((x) << s) | ((x) >> (64 - s))) -#define REPEAT6(e) e e e e e e -#define REPEAT24(e) REPEAT6(e e e e) -#define REPEAT5(e) e e e e e -#define FOR5(v, s, e) \ - v = 0; \ - REPEAT5(e; v += s;) - -/*** Keccak-f[1600] ***/ -static inline void keccakf(void* state) { - uint64_t* a = (uint64_t*)state; - uint64_t b[5] = {0}; - uint64_t t = 0; - uint8_t x, y; - int i; - - for (i = 0; i < 24; i++) { - // Theta - FOR5(x, 1, - b[x] = 0; - FOR5(y, 5, - b[x] ^= a[x + y]; )) - FOR5(x, 1, - FOR5(y, 5, - a[y + x] ^= b[(x + 4) % 5] ^ rol(b[(x + 1) % 5], 1); )) - // Rho and pi - t = a[1]; - x = 0; - REPEAT24(b[0] = a[pi[x]]; - a[pi[x]] = rol(t, rho[x]); - t = b[0]; - x++; ) - // Chi - FOR5(y, - 5, - FOR5(x, 1, - b[x] = a[y + x];) - FOR5(x, 1, - a[y + x] = b[x] ^ ((~b[(x + 1) % 5]) & b[(x + 2) % 5]); )) - // Iota - a[0] ^= RC[i]; - } -} - -/******** The FIPS202-defined functions. ********/ - -/*** Some helper macros. ***/ - -#define _(S) do { S } while (0) -#define FOR(i, ST, L, S) \ - _({size_t i; for (i = 0; i < L; i += ST) { S; }}) -#define mkapply_ds(NAME, S) \ - static inline void NAME(uint8_t* dst, \ - const uint8_t* src, \ - size_t len) { \ - FOR(i, 1, len, S); \ - } -#define mkapply_sd(NAME, S) \ - static inline void NAME(const uint8_t* src, \ - uint8_t* dst, \ - size_t len) { \ - FOR(i, 1, len, S); \ - } - -mkapply_ds(xorin, dst[i] ^= src[i]) // xorin -mkapply_sd(setout, dst[i] = src[i]) // setout - -#define P keccakf -#define Plen 200 - -// Fold P*F over the full blocks of an input. -#define foldP(I, L, F) \ - while (L >= rate) { \ - F(a, I, rate); \ - P(a); \ - I += rate; \ - L -= rate; \ - } - -/** The sponge-based hash construction. **/ -static inline int hash(uint8_t* out, size_t outlen, - const uint8_t* in, size_t inlen, - size_t rate, uint8_t delim) { - if ((out == NULL) || ((in == NULL) && inlen != 0) || (rate >= Plen)) { - return -1; - } - uint8_t a[Plen] = {0}; - // Absorb input. - foldP(in, inlen, xorin); - // Xor in the DS and pad frame. - a[inlen] ^= delim; - a[rate - 1] ^= 0x80; - // Xor in the last block. - xorin(a, in, inlen); - // Apply P - P(a); - // Squeeze output. - foldP(out, outlen, setout); - setout(a, out, outlen); - memset(a, 0, 200); - return 0; -} - -/*** Helper macros to define SHA3 and SHAKE instances. ***/ -#define defshake(bits) \ - int shake##bits(uint8_t* out, size_t outlen, \ - const uint8_t* in, size_t inlen) { \ - return hash(out, outlen, in, inlen, 200 - (bits / 4), 0x1f); \ - } -#define defkeccak(bits) \ - int keccak_##bits(uint8_t* out, size_t outlen, \ - const uint8_t* in, size_t inlen) { \ - if (outlen > (bits/8)) { \ - return -1; \ - } \ - return hash(out, outlen, in, inlen, 200 - (bits / 4), 0x01); \ - } - -/*** FIPS202 SHAKE VOFs ***/ -defshake(128) -defshake(256) - -/*** FIPS202 SHA3 FOFs ***/ -defkeccak(224) -defkeccak(256) -defkeccak(384) -defkeccak(512) - - - diff --git a/util/hashdb/Cargo.toml b/util/hashdb/Cargo.toml index e6250fb56a1a52207f55c5a01ffbf258995b1810..d4e055f9ff0e844248703b85d7f010cd593238fb 100644 --- a/util/hashdb/Cargo.toml +++ b/util/hashdb/Cargo.toml @@ -7,4 +7,4 @@ license = "GPL-3.0" [dependencies] elastic-array = "0.10" -ethereum-types = "0.2" +ethereum-types = "0.3" diff --git a/util/hashdb/src/lib.rs b/util/hashdb/src/lib.rs index b65f304e423673f0d7dcb9b84a264b602866dca4..182e81c5dc033ed9b95b8a6f82ec05763cd7ee12 100644 --- a/util/hashdb/src/lib.rs +++ b/util/hashdb/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/util/io/Cargo.toml b/util/io/Cargo.toml index dca0426d509fa321ff63349361b19374f1dbb7f9..71688661605f4f5c457c383bbe2c656f6d136357 100644 --- a/util/io/Cargo.toml +++ b/util/io/Cargo.toml @@ -3,13 +3,16 @@ description = "Ethcore IO library" homepage = "http://parity.io" license = "GPL-3.0" name = "ethcore-io" -version = "1.11.0" +version = "1.12.0" authors = ["Parity Technologies "] [dependencies] -mio = "0.6.8" +fnv = "1.0" +mio = { version = "0.6.8", optional = true } crossbeam = "0.3" parking_lot = "0.5" log = "0.3" -slab = "0.2" - +slab = "0.4" +num_cpus = "1.8" +timer = "0.2" +time = "0.1" diff --git a/util/io/src/lib.rs b/util/io/src/lib.rs index 22241a2f5d0902749bd5aed4952b05d1538d1052..02dbf223be408ffd70bb690f8eb289d61f65ed7b 100644 --- a/util/io/src/lib.rs +++ b/util/io/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -22,6 +22,7 @@ //! extern crate ethcore_io; //! use ethcore_io::*; //! use std::sync::Arc; +//! use std::time::Duration; //! //! struct MyHandler; //! @@ -32,7 +33,7 @@ //! //! impl IoHandler for MyHandler { //! fn initialize(&self, io: &IoContext) { -//! io.register_timer(0, 1000).unwrap(); +//! io.register_timer(0, Duration::from_secs(1)).unwrap(); //! } //! //! fn timeout(&self, _io: &IoContext, timer: TimerToken) { @@ -53,30 +54,59 @@ //! // Drop the service //! } //! ``` +//! +//! # Mio vs non-mio +//! +//! This library has two modes: mio and not mio. The `mio` feature can be activated or deactivated +//! when compiling or depending on the library. +//! +//! Without mio, only timers and message-passing are available. With mio, you can also use +//! low-level sockets provided by mio. +//! +//! The non-mio mode exists because the `mio` library doesn't compile on platforms such as +//! emscripten. //TODO: use Poll from mio #![allow(deprecated)] +#[cfg(feature = "mio")] extern crate mio; #[macro_use] extern crate log as rlog; extern crate slab; extern crate crossbeam; extern crate parking_lot; +extern crate num_cpus; +extern crate timer; +extern crate fnv; +extern crate time; -mod service; +#[cfg(feature = "mio")] +mod service_mio; +#[cfg(not(feature = "mio"))] +mod service_non_mio; +#[cfg(feature = "mio")] mod worker; +use std::cell::Cell; use std::{fmt, error}; +#[cfg(feature = "mio")] use mio::deprecated::{EventLoop, NotifyError}; +#[cfg(feature = "mio")] use mio::Token; -pub use worker::LOCAL_STACK_SIZE; +thread_local! { + /// Stack size + /// Should be modified if it is changed in Rust since it is no way + /// to know or get it + pub static LOCAL_STACK_SIZE: Cell = Cell::new(::std::env::var("RUST_MIN_STACK").ok().and_then(|s| s.parse().ok()).unwrap_or(2 * 1024 * 1024)); +} #[derive(Debug)] /// IO Error pub enum IoError { /// Low level error from mio crate + #[cfg(feature = "mio")] Mio(::std::io::Error), /// Error concerning the Rust standard library's IO subsystem. StdIo(::std::io::Error), @@ -87,6 +117,7 @@ impl fmt::Display for IoError { // just defer to the std implementation for now. // we can refine the formatting when more variants are added. match *self { + #[cfg(feature = "mio")] IoError::Mio(ref std_err) => std_err.fmt(f), IoError::StdIo(ref std_err) => std_err.fmt(f), } @@ -105,8 +136,9 @@ impl From<::std::io::Error> for IoError { } } -impl From>> for IoError where Message: Send + Clone { - fn from(_err: NotifyError>) -> IoError { +#[cfg(feature = "mio")] +impl From>> for IoError where Message: Send { + fn from(_err: NotifyError>) -> IoError { IoError::Mio(::std::io::Error::new(::std::io::ErrorKind::ConnectionAborted, "Network IO notification error")) } } @@ -114,7 +146,7 @@ impl From>> for IoError where M /// Generic IO handler. /// All the handler function are called from within IO event loop. /// `Message` type is used as notification data -pub trait IoHandler: Send + Sync where Message: Send + Sync + Clone + 'static { +pub trait IoHandler: Send + Sync where Message: Send + Sync + 'static { /// Initialize the handler fn initialize(&self, _io: &IoContext) {} /// Timer function called after a timeout created with `HandlerIo::timeout`. @@ -122,57 +154,125 @@ pub trait IoHandler: Send + Sync where Message: Send + Sync + Clone + ' /// Called when a broadcasted message is received. The message can only be sent from a different IO handler. fn message(&self, _io: &IoContext, _message: &Message) {} /// Called when an IO stream gets closed + #[cfg(feature = "mio")] fn stream_hup(&self, _io: &IoContext, _stream: StreamToken) {} /// Called when an IO stream can be read from + #[cfg(feature = "mio")] fn stream_readable(&self, _io: &IoContext, _stream: StreamToken) {} /// Called when an IO stream can be written to + #[cfg(feature = "mio")] fn stream_writable(&self, _io: &IoContext, _stream: StreamToken) {} /// Register a new stream with the event loop + #[cfg(feature = "mio")] fn register_stream(&self, _stream: StreamToken, _reg: Token, _event_loop: &mut EventLoop>) {} /// Re-register a stream with the event loop + #[cfg(feature = "mio")] fn update_stream(&self, _stream: StreamToken, _reg: Token, _event_loop: &mut EventLoop>) {} /// Deregister a stream. Called whenstream is removed from event loop + #[cfg(feature = "mio")] fn deregister_stream(&self, _stream: StreamToken, _event_loop: &mut EventLoop>) {} } -pub use service::TimerToken; -pub use service::StreamToken; -pub use service::IoContext; -pub use service::IoService; -pub use service::IoChannel; -pub use service::IoManager; -pub use service::TOKENS_PER_HANDLER; +#[cfg(feature = "mio")] +pub use service_mio::{TimerToken, StreamToken, IoContext, IoService, IoChannel, IoManager, TOKENS_PER_HANDLER}; +#[cfg(not(feature = "mio"))] +pub use service_non_mio::{TimerToken, IoContext, IoService, IoChannel, TOKENS_PER_HANDLER}; #[cfg(test)] mod tests { - use std::sync::Arc; + use std::sync::atomic; + use std::thread; + use std::time::Duration; use super::*; - struct MyHandler; + // Mio's behaviour is too unstable for this test. Sometimes we have to wait a few milliseconds, + // sometimes more than 5 seconds for the message to arrive. + // Therefore we ignore this test in order to not have spurious failure when running continuous + // integration. + #[test] + #[cfg_attr(feature = "mio", ignore)] + fn send_message_to_handler() { + struct MyHandler(atomic::AtomicBool); - #[derive(Clone)] - struct MyMessage { - data: u32 - } + #[derive(Clone)] + struct MyMessage { + data: u32 + } - impl IoHandler for MyHandler { - fn initialize(&self, io: &IoContext) { - io.register_timer(0, 1000).unwrap(); + impl IoHandler for MyHandler { + fn message(&self, _io: &IoContext, message: &MyMessage) { + assert_eq!(message.data, 5); + self.0.store(true, atomic::Ordering::SeqCst); + } } - fn timeout(&self, _io: &IoContext, timer: TimerToken) { - println!("Timeout {}", timer); + let handler = Arc::new(MyHandler(atomic::AtomicBool::new(false))); + + let service = IoService::::start().expect("Error creating network service"); + service.register_handler(handler.clone()).unwrap(); + + service.send_message(MyMessage { data: 5 }).unwrap(); + + thread::sleep(Duration::from_secs(1)); + assert!(handler.0.load(atomic::Ordering::SeqCst)); + } + + #[test] + fn timeout_working() { + struct MyHandler(atomic::AtomicBool); + + #[derive(Clone)] + struct MyMessage { + data: u32 } - fn message(&self, _io: &IoContext, message: &MyMessage) { - println!("Message {}", message.data); + impl IoHandler for MyHandler { + fn initialize(&self, io: &IoContext) { + io.register_timer_once(1234, Duration::from_millis(500)).unwrap(); + } + + fn timeout(&self, _io: &IoContext, timer: TimerToken) { + assert_eq!(timer, 1234); + assert!(!self.0.swap(true, atomic::Ordering::SeqCst)); + } } + + let handler = Arc::new(MyHandler(atomic::AtomicBool::new(false))); + + let service = IoService::::start().expect("Error creating network service"); + service.register_handler(handler.clone()).unwrap(); + + thread::sleep(Duration::from_secs(2)); + assert!(handler.0.load(atomic::Ordering::SeqCst)); } #[test] - fn test_service_register_handler () { + fn multi_timeout_working() { + struct MyHandler(atomic::AtomicUsize); + + #[derive(Clone)] + struct MyMessage { + data: u32 + } + + impl IoHandler for MyHandler { + fn initialize(&self, io: &IoContext) { + io.register_timer(1234, Duration::from_millis(500)).unwrap(); + } + + fn timeout(&self, _io: &IoContext, timer: TimerToken) { + assert_eq!(timer, 1234); + self.0.fetch_add(1, atomic::Ordering::SeqCst); + } + } + + let handler = Arc::new(MyHandler(atomic::AtomicUsize::new(0))); + let service = IoService::::start().expect("Error creating network service"); - service.register_handler(Arc::new(MyHandler)).unwrap(); + service.register_handler(handler.clone()).unwrap(); + + thread::sleep(Duration::from_secs(2)); + assert!(handler.0.load(atomic::Ordering::SeqCst) >= 2); } } diff --git a/util/io/src/service.rs b/util/io/src/service_mio.rs similarity index 84% rename from util/io/src/service.rs rename to util/io/src/service_mio.rs index baa279cabdbdd4e457131deb3e03b829ada5e3a6..089d54cc45859d9cf313fe6bbe6338b1fbdf242d 100644 --- a/util/io/src/service.rs +++ b/util/io/src/service_mio.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -41,7 +41,7 @@ const MAX_HANDLERS: usize = 8; /// Messages used to communicate with the event loop from other threads. #[derive(Clone)] -pub enum IoMessage where Message: Send + Clone + Sized { +pub enum IoMessage where Message: Send + Sized { /// Shutdown the event loop Shutdown, /// Register a new protocol handler. @@ -54,7 +54,7 @@ pub enum IoMessage where Message: Send + Clone + Sized { AddTimer { handler_id: HandlerId, token: TimerToken, - delay: u64, + delay: Duration, once: bool, }, RemoveTimer { @@ -74,16 +74,16 @@ pub enum IoMessage where Message: Send + Clone + Sized { token: StreamToken, }, /// Broadcast a message across all protocol handlers. - UserMessage(Message) + UserMessage(Arc) } /// IO access point. This is passed to all IO handlers and provides an interface to the IO subsystem. -pub struct IoContext where Message: Send + Clone + Sync + 'static { +pub struct IoContext where Message: Send + Sync + 'static { channel: IoChannel, handler: HandlerId, } -impl IoContext where Message: Send + Clone + Sync + 'static { +impl IoContext where Message: Send + Sync + 'static { /// Create a new IO access point. Takes references to all the data that can be updated within the IO handler. pub fn new(channel: IoChannel, handler: HandlerId) -> IoContext { IoContext { @@ -93,10 +93,10 @@ impl IoContext where Message: Send + Clone + Sync + 'static { } /// Register a new recurring IO timer. 'IoHandler::timeout' will be called with the token. - pub fn register_timer(&self, token: TimerToken, ms: u64) -> Result<(), IoError> { + pub fn register_timer(&self, token: TimerToken, delay: Duration) -> Result<(), IoError> { self.channel.send_io(IoMessage::AddTimer { - token: token, - delay: ms, + token, + delay, handler_id: self.handler, once: false, })?; @@ -104,10 +104,10 @@ impl IoContext where Message: Send + Clone + Sync + 'static { } /// Register a new IO timer once. 'IoHandler::timeout' will be called with the token. - pub fn register_timer_once(&self, token: TimerToken, ms: u64) -> Result<(), IoError> { + pub fn register_timer_once(&self, token: TimerToken, delay: Duration) -> Result<(), IoError> { self.channel.send_io(IoMessage::AddTimer { - token: token, - delay: ms, + token, + delay, handler_id: self.handler, once: true, })?; @@ -162,18 +162,20 @@ impl IoContext where Message: Send + Clone + Sync + 'static { } /// Unregister current IO handler. - pub fn unregister_handler(&self) -> Result<(), IoError> { - self.channel.send_io(IoMessage::RemoveHandler { + pub fn unregister_handler(&self) { + // `send_io` returns an error only if the channel is closed, which means that the + // background thread is no longer running. Therefore the handler is no longer active and + // can be considered as unregistered. + let _ = self.channel.send_io(IoMessage::RemoveHandler { handler_id: self.handler, - })?; - Ok(()) + }); } } #[derive(Clone)] struct UserTimer { - delay: u64, + delay: Duration, timeout: Timeout, once: bool, } @@ -181,17 +183,17 @@ struct UserTimer { /// Root IO handler. Manages user handlers, messages and IO timers. pub struct IoManager where Message: Send + Sync { timers: Arc>>, - handlers: Arc>, HandlerId>>>, + handlers: Arc>>>>, workers: Vec, worker_channel: chase_lev::Worker>, work_ready: Arc, } -impl IoManager where Message: Send + Sync + Clone + 'static { +impl IoManager where Message: Send + Sync + 'static { /// Creates a new instance and registers it with the event loop. pub fn start( event_loop: &mut EventLoop>, - handlers: Arc>, HandlerId>>> + handlers: Arc>>>> ) -> Result<(), IoError> { let (worker, stealer) = chase_lev::deque(); let num_workers = 4; @@ -219,7 +221,7 @@ impl IoManager where Message: Send + Sync + Clone + 'static { } } -impl Handler for IoManager where Message: Send + Clone + Sync + 'static { +impl Handler for IoManager where Message: Send + Sync + 'static { type Timeout = Token; type Message = IoMessage; @@ -252,7 +254,7 @@ impl Handler for IoManager where Message: Send + Clone + Sync self.timers.write().remove(&token_id); event_loop.clear_timeout(&timer.timeout); } else { - event_loop.timeout(token, Duration::from_millis(timer.delay)).expect("Error re-registering user timer"); + event_loop.timeout(token, timer.delay).expect("Error re-registering user timer"); } self.worker_channel.push(Work { work_type: WorkType::Timeout, token: token_id, handler: handler.clone(), handler_id: handler_index }); self.work_ready.notify_all(); @@ -267,7 +269,8 @@ impl Handler for IoManager where Message: Send + Clone + Sync event_loop.shutdown(); }, IoMessage::AddHandler { handler } => { - let handler_id = self.handlers.write().insert(handler.clone()).unwrap_or_else(|_| panic!("Too many handlers registered")); + let handler_id = self.handlers.write().insert(handler.clone()); + assert!(handler_id <= MAX_HANDLERS, "Too many handlers registered"); handler.initialize(&IoContext::new(IoChannel::new(event_loop.channel(), Arc::downgrade(&self.handlers)), handler_id)); }, IoMessage::RemoveHandler { handler_id } => { @@ -283,7 +286,7 @@ impl Handler for IoManager where Message: Send + Clone + Sync }, IoMessage::AddTimer { handler_id, token, delay, once } => { let timer_id = token + handler_id * TOKENS_PER_HANDLER; - let timeout = event_loop.timeout(Token(timer_id), Duration::from_millis(delay)).expect("Error registering user timer"); + let timeout = event_loop.timeout(Token(timer_id), delay).expect("Error registering user timer"); self.timers.write().insert(timer_id, UserTimer { delay: delay, timeout: timeout, once: once }); }, IoMessage::RemoveTimer { handler_id, token } => { @@ -317,7 +320,12 @@ impl Handler for IoManager where Message: Send + Clone + Sync for id in 0 .. MAX_HANDLERS { if let Some(h) = self.handlers.read().get(id) { let handler = h.clone(); - self.worker_channel.push(Work { work_type: WorkType::Message(data.clone()), token: 0, handler: handler, handler_id: id }); + self.worker_channel.push(Work { + work_type: WorkType::Message(data.clone()), + token: 0, + handler: handler, + handler_id: id + }); } } self.work_ready.notify_all(); @@ -326,21 +334,30 @@ impl Handler for IoManager where Message: Send + Clone + Sync } } -#[derive(Clone)] -enum Handlers where Message: Send + Clone { - SharedCollection(Weak>, HandlerId>>>), +enum Handlers where Message: Send { + SharedCollection(Weak>>>>), Single(Weak>), } +impl Clone for Handlers { + fn clone(&self) -> Self { + use self::Handlers::*; + + match *self { + SharedCollection(ref w) => SharedCollection(w.clone()), + Single(ref w) => Single(w.clone()), + } + } +} + /// Allows sending messages into the event loop. All the IO handlers will get the message /// in the `message` callback. -pub struct IoChannel where Message: Send + Clone{ +pub struct IoChannel where Message: Send { channel: Option>>, handlers: Handlers, - } -impl Clone for IoChannel where Message: Send + Clone + Sync + 'static { +impl Clone for IoChannel where Message: Send + Sync + 'static { fn clone(&self) -> IoChannel { IoChannel { channel: self.channel.clone(), @@ -349,11 +366,11 @@ impl Clone for IoChannel where Message: Send + Clone + Sync + } } -impl IoChannel where Message: Send + Clone + Sync + 'static { +impl IoChannel where Message: Send + Sync + 'static { /// Send a message through the channel pub fn send(&self, message: Message) -> Result<(), IoError> { match self.channel { - Some(ref channel) => channel.send(IoMessage::UserMessage(message))?, + Some(ref channel) => channel.send(IoMessage::UserMessage(Arc::new(message)))?, None => self.send_sync(message)? } Ok(()) @@ -403,7 +420,7 @@ impl IoChannel where Message: Send + Clone + Sync + 'static { handlers: Handlers::Single(handler), } } - fn new(channel: Sender>, handlers: Weak>, HandlerId>>>) -> IoChannel { + fn new(channel: Sender>, handlers: Weak>>>>) -> IoChannel { IoChannel { channel: Some(channel), handlers: Handlers::SharedCollection(handlers), @@ -413,20 +430,20 @@ impl IoChannel where Message: Send + Clone + Sync + 'static { /// General IO Service. Starts an event loop and dispatches IO requests. /// 'Message' is a notification message type -pub struct IoService where Message: Send + Sync + Clone + 'static { +pub struct IoService where Message: Send + Sync + 'static { thread: Mutex>>, host_channel: Mutex>>, - handlers: Arc>, HandlerId>>>, + handlers: Arc>>>>, } -impl IoService where Message: Send + Sync + Clone + 'static { +impl IoService where Message: Send + Sync + 'static { /// Starts IO event loop pub fn start() -> Result, IoError> { let mut config = EventLoopBuilder::new(); config.messages_per_tick(1024); let mut event_loop = config.build().expect("Error creating event loop"); let channel = event_loop.channel(); - let handlers = Arc::new(RwLock::new(Slab::new(MAX_HANDLERS))); + let handlers = Arc::new(RwLock::new(Slab::with_capacity(MAX_HANDLERS))); let h = handlers.clone(); let thread = thread::spawn(move || { IoManager::::start(&mut event_loop, h).expect("Error starting IO service"); @@ -462,7 +479,7 @@ impl IoService where Message: Send + Sync + Clone + 'static { /// Send a message over the network. Normaly `HostIo::send` should be used. This can be used from non-io threads. pub fn send_message(&self, message: Message) -> Result<(), IoError> { - self.host_channel.lock().send(IoMessage::UserMessage(message))?; + self.host_channel.lock().send(IoMessage::UserMessage(Arc::new(message)))?; Ok(()) } @@ -472,9 +489,8 @@ impl IoService where Message: Send + Sync + Clone + 'static { } } -impl Drop for IoService where Message: Send + Sync + Clone { +impl Drop for IoService where Message: Send + Sync { fn drop(&mut self) { self.stop() } } - diff --git a/util/io/src/service_non_mio.rs b/util/io/src/service_non_mio.rs new file mode 100644 index 0000000000000000000000000000000000000000..315f84c4d1d0822068a91ee506fc781522ee0588 --- /dev/null +++ b/util/io/src/service_non_mio.rs @@ -0,0 +1,334 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use std::sync::{Arc, Weak}; +use std::thread; +use crossbeam::sync::chase_lev; +use slab::Slab; +use fnv::FnvHashMap; +use {IoError, IoHandler}; +use parking_lot::{RwLock, Mutex}; +use num_cpus; +use std::time::Duration; +use timer::{Timer, Guard as TimerGuard}; +use time::Duration as TimeDuration; + +/// Timer ID +pub type TimerToken = usize; +/// IO Handler ID +pub type HandlerId = usize; + +/// Maximum number of tokens a handler can use +pub const TOKENS_PER_HANDLER: usize = 16384; +const MAX_HANDLERS: usize = 8; + +/// IO access point. This is passed to all IO handlers and provides an interface to the IO subsystem. +pub struct IoContext where Message: Send + Sync + 'static { + handler: HandlerId, + shared: Arc>, +} + +impl IoContext where Message: Send + Sync + 'static { + /// Register a new recurring IO timer. 'IoHandler::timeout' will be called with the token. + pub fn register_timer(&self, token: TimerToken, delay: Duration) -> Result<(), IoError> { + let channel = self.channel(); + + let msg = WorkTask::TimerTrigger { + handler_id: self.handler, + token: token, + }; + + let delay = TimeDuration::from_std(delay) + .map_err(|e| ::std::io::Error::new(::std::io::ErrorKind::Other, e))?; + let guard = self.shared.timer.lock().schedule_repeating(delay, move || { + channel.send_raw(msg.clone()); + }); + + self.shared.timers.lock().insert(token, guard); + + Ok(()) + } + + /// Register a new IO timer once. 'IoHandler::timeout' will be called with the token. + pub fn register_timer_once(&self, token: TimerToken, delay: Duration) -> Result<(), IoError> { + let channel = self.channel(); + + let msg = WorkTask::TimerTrigger { + handler_id: self.handler, + token: token, + }; + + let delay = TimeDuration::from_std(delay) + .map_err(|e| ::std::io::Error::new(::std::io::ErrorKind::Other, e))?; + let guard = self.shared.timer.lock().schedule_with_delay(delay, move || { + channel.send_raw(msg.clone()); + }); + + self.shared.timers.lock().insert(token, guard); + + Ok(()) + } + + /// Delete a timer. + pub fn clear_timer(&self, token: TimerToken) -> Result<(), IoError> { + self.shared.timers.lock().remove(&token); + Ok(()) + } + + /// Broadcast a message to other IO clients + pub fn message(&self, message: Message) -> Result<(), IoError> { + if let Some(ref channel) = *self.shared.channel.lock() { + channel.push(WorkTask::UserMessage(Arc::new(message))); + } + for thread in self.shared.threads.read().iter() { + thread.unpark(); + } + + Ok(()) + } + + /// Get message channel + pub fn channel(&self) -> IoChannel { + IoChannel { shared: Arc::downgrade(&self.shared) } + } + + /// Unregister current IO handler. + pub fn unregister_handler(&self) -> Result<(), IoError> { + self.shared.handlers.write().remove(self.handler); + Ok(()) + } +} + +/// Allows sending messages into the event loop. All the IO handlers will get the message +/// in the `message` callback. +pub struct IoChannel where Message: Send + Sync + 'static { + shared: Weak>, +} + +impl Clone for IoChannel where Message: Send + Sync + 'static { + fn clone(&self) -> IoChannel { + IoChannel { + shared: self.shared.clone(), + } + } +} + +impl IoChannel where Message: Send + Sync + 'static { + /// Send a message through the channel + pub fn send(&self, message: Message) -> Result<(), IoError> { + if let Some(shared) = self.shared.upgrade() { + match *shared.channel.lock() { + Some(ref channel) => channel.push(WorkTask::UserMessage(Arc::new(message))), + None => self.send_sync(message)? + }; + + for thread in shared.threads.read().iter() { + thread.unpark(); + } + } + + Ok(()) + } + + /// Send a message through the channel and handle it synchronously + pub fn send_sync(&self, message: Message) -> Result<(), IoError> { + if let Some(shared) = self.shared.upgrade() { + for id in 0 .. MAX_HANDLERS { + if let Some(h) = shared.handlers.read().get(id) { + let handler = h.clone(); + let ctxt = IoContext { handler: id, shared: shared.clone() }; + handler.message(&ctxt, &message); + } + } + } + + Ok(()) + } + + // Send low level io message + fn send_raw(&self, message: WorkTask) { + if let Some(shared) = self.shared.upgrade() { + if let Some(ref channel) = *shared.channel.lock() { + channel.push(message); + } + + for thread in shared.threads.read().iter() { + thread.unpark(); + } + } + } + + /// Create a new channel disconnected from an event loop. + pub fn disconnected() -> IoChannel { + IoChannel { + shared: Weak::default(), + } + } +} + +/// General IO Service. Starts an event loop and dispatches IO requests. +/// 'Message' is a notification message type +pub struct IoService where Message: Send + Sync + 'static { + thread_joins: Mutex>>, + shared: Arc>, +} + +// Struct shared throughout the whole implementation. +struct Shared where Message: Send + Sync + 'static { + // All the I/O handlers that have been registered. + handlers: RwLock>>>, + // All the background threads, so that we can unpark them. + threads: RwLock>, + // Used to create timeouts. + timer: Mutex, + // List of created timers. We need to keep them in a data struct so that we can cancel them if + // necessary. + timers: Mutex>, + // Channel used to send work to the worker threads. + channel: Mutex>>>, +} + +// Messages used to communicate with the event loop from other threads. +enum WorkTask where Message: Send + Sized { + Shutdown, + TimerTrigger { + handler_id: HandlerId, + token: TimerToken, + }, + UserMessage(Arc) +} + +impl Clone for WorkTask where Message: Send + Sized { + fn clone(&self) -> WorkTask { + match *self { + WorkTask::Shutdown => WorkTask::Shutdown, + WorkTask::TimerTrigger { handler_id, token } => WorkTask::TimerTrigger { handler_id, token }, + WorkTask::UserMessage(ref msg) => WorkTask::UserMessage(msg.clone()), + } + } +} + +impl IoService where Message: Send + Sync + 'static { + /// Starts IO event loop + pub fn start() -> Result, IoError> { + let (tx, rx) = chase_lev::deque(); + + let shared = Arc::new(Shared { + handlers: RwLock::new(Slab::with_capacity(MAX_HANDLERS)), + threads: RwLock::new(Vec::new()), + timer: Mutex::new(Timer::new()), + timers: Mutex::new(FnvHashMap::default()), + channel: Mutex::new(Some(tx)), + }); + + let thread_joins = (0 .. num_cpus::get()).map(|_| { + let rx = rx.clone(); + let shared = shared.clone(); + thread::spawn(move || { + do_work(&shared, rx) + }) + }).collect::>(); + + *shared.threads.write() = thread_joins.iter().map(|t| t.thread().clone()).collect(); + + Ok(IoService { + thread_joins: Mutex::new(thread_joins), + shared, + }) + } + + /// Stops the IO service. + pub fn stop(&self) { + trace!(target: "shutdown", "[IoService] Closing..."); + // Clear handlers so that shared pointers are not stuck on stack + // in Channel::send_sync + self.shared.handlers.write().clear(); + let channel = self.shared.channel.lock().take(); + let mut thread_joins = self.thread_joins.lock(); + if let Some(channel) = channel { + for _ in 0 .. thread_joins.len() { + channel.push(WorkTask::Shutdown); + } + } + for thread in thread_joins.drain(..) { + thread.thread().unpark(); + thread.join().unwrap_or_else(|e| { + debug!(target: "shutdown", "Error joining IO service worker thread: {:?}", e); + }); + } + trace!(target: "shutdown", "[IoService] Closed."); + } + + /// Register an IO handler with the event loop. + pub fn register_handler(&self, handler: Arc+Send>) -> Result<(), IoError> { + let id = self.shared.handlers.write().insert(handler.clone()); + assert!(id <= MAX_HANDLERS, "Too many handlers registered"); + let ctxt = IoContext { handler: id, shared: self.shared.clone() }; + handler.initialize(&ctxt); + Ok(()) + } + + /// Send a message over the network. Normaly `HostIo::send` should be used. This can be used from non-io threads. + pub fn send_message(&self, message: Message) -> Result<(), IoError> { + if let Some(ref channel) = *self.shared.channel.lock() { + channel.push(WorkTask::UserMessage(Arc::new(message))); + } + for thread in self.shared.threads.read().iter() { + thread.unpark(); + } + Ok(()) + } + + /// Create a new message channel + #[inline] + pub fn channel(&self) -> IoChannel { + IoChannel { + shared: Arc::downgrade(&self.shared) + } + } +} + +impl Drop for IoService where Message: Send + Sync { + fn drop(&mut self) { + self.stop() + } +} + +fn do_work(shared: &Arc>, rx: chase_lev::Stealer>) + where Message: Send + Sync + 'static +{ + loop { + match rx.steal() { + chase_lev::Steal::Abort => continue, + chase_lev::Steal::Empty => thread::park(), + chase_lev::Steal::Data(WorkTask::Shutdown) => break, + chase_lev::Steal::Data(WorkTask::UserMessage(message)) => { + for id in 0 .. MAX_HANDLERS { + if let Some(handler) = shared.handlers.read().get(id) { + let ctxt = IoContext { handler: id, shared: shared.clone() }; + handler.message(&ctxt, &message); + } + } + }, + chase_lev::Steal::Data(WorkTask::TimerTrigger { handler_id, token }) => { + if let Some(handler) = shared.handlers.read().get(handler_id) { + let ctxt = IoContext { handler: handler_id, shared: shared.clone() }; + handler.timeout(&ctxt, token); + } + }, + } + } +} diff --git a/util/io/src/worker.rs b/util/io/src/worker.rs index 79570d3612a741ad598e4e1baeb1dd16a0409845..da144afea492ee0e213b5ad3f19f1687e10a6c8f 100644 --- a/util/io/src/worker.rs +++ b/util/io/src/worker.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -18,27 +18,20 @@ use std::sync::Arc; use std::thread::{JoinHandle, self}; use std::sync::atomic::{AtomicBool, Ordering as AtomicOrdering}; use crossbeam::sync::chase_lev; -use service::{HandlerId, IoChannel, IoContext}; +use service_mio::{HandlerId, IoChannel, IoContext}; use IoHandler; -use std::cell::Cell; +use LOCAL_STACK_SIZE; use std::sync::{Condvar as SCondvar, Mutex as SMutex}; const STACK_SIZE: usize = 16*1024*1024; -thread_local! { - /// Stack size - /// Should be modified if it is changed in Rust since it is no way - /// to know or get it - pub static LOCAL_STACK_SIZE: Cell = Cell::new(::std::env::var("RUST_MIN_STACK").ok().and_then(|s| s.parse().ok()).unwrap_or(2 * 1024 * 1024)); -} - pub enum WorkType { Readable, Writable, Hup, Timeout, - Message(Message) + Message(Arc) } pub struct Work { @@ -65,7 +58,7 @@ impl Worker { wait: Arc, wait_mutex: Arc>, ) -> Worker - where Message: Send + Sync + Clone + 'static { + where Message: Send + Sync + 'static { let deleting = Arc::new(AtomicBool::new(false)); let mut worker = Worker { thread: None, @@ -86,7 +79,7 @@ impl Worker { channel: IoChannel, wait: Arc, wait_mutex: Arc>, deleting: Arc) - where Message: Send + Sync + Clone + 'static { + where Message: Send + Sync + 'static { loop { { let lock = wait_mutex.lock().expect("Poisoned work_loop mutex"); @@ -105,7 +98,7 @@ impl Worker { } } - fn do_work(work: Work, channel: IoChannel) where Message: Send + Sync + Clone + 'static { + fn do_work(work: Work, channel: IoChannel) where Message: Send + Sync + 'static { match work.work_type { WorkType::Readable => { work.handler.stream_readable(&IoContext::new(channel, work.handler_id), work.token); @@ -120,7 +113,7 @@ impl Worker { work.handler.timeout(&IoContext::new(channel, work.handler_id), work.token); } WorkType::Message(message) => { - work.handler.message(&IoContext::new(channel, work.handler_id), &message); + work.handler.message(&IoContext::new(channel, work.handler_id), &*message); } } } diff --git a/util/journaldb/Cargo.toml b/util/journaldb/Cargo.toml index d3d4708f044425bf1b3352f928381ff90b0f7e6c..dea70bd6a4361835a7fbb00e709a7ca7c62d5891 100644 --- a/util/journaldb/Cargo.toml +++ b/util/journaldb/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL3" [dependencies] ethcore-bytes = { path = "../bytes" } -ethereum-types = "0.2" +ethereum-types = "0.3" hashdb = { path = "../hashdb" } heapsize = "0.4" kvdb = { path = "../kvdb" } diff --git a/util/journaldb/src/archivedb.rs b/util/journaldb/src/archivedb.rs index e00b37d3c4afcd28f836924e7254427492faabb4..e2d8c80070939eb8f54c94dc5388cec904efba7c 100644 --- a/util/journaldb/src/archivedb.rs +++ b/util/journaldb/src/archivedb.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -45,14 +45,15 @@ pub struct ArchiveDB { impl ArchiveDB { /// Create a new instance from a key-value db. - pub fn new(backing: Arc, col: Option) -> ArchiveDB { - let latest_era = backing.get(col, &LATEST_ERA_KEY).expect("Low-level database error.") - .map(|val| decode::(&val)); + pub fn new(backing: Arc, column: Option) -> ArchiveDB { + let latest_era = backing.get(column, &LATEST_ERA_KEY) + .expect("Low-level database error.") + .map(|val| decode::(&val).expect("decoding db value failed")); ArchiveDB { overlay: MemoryDB::new(), - backing: backing, - latest_era: latest_era, - column: col, + backing, + latest_era, + column, } } diff --git a/util/journaldb/src/earlymergedb.rs b/util/journaldb/src/earlymergedb.rs index bb8e49d41edfe2a6eec6ed5999e930429f17f6f4..25e078bdae1dbfeed941fbc3ee113ada1435c771 100644 --- a/util/journaldb/src/earlymergedb.rs +++ b/util/journaldb/src/earlymergedb.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -57,7 +57,7 @@ enum RemoveFrom { /// the removals actually take effect. /// /// journal format: -/// ``` +/// ```text /// [era, 0] => [ id, [insert_0, ...], [remove_0, ...] ] /// [era, 1] => [ id, [insert_0, ...], [remove_0, ...] ] /// [era, n] => [ ... ] @@ -76,7 +76,7 @@ enum RemoveFrom { /// which includes an original key, if any. /// /// The semantics of the `counter` are: -/// ``` +/// ```text /// insert key k: /// counter already contains k: count += 1 /// counter doesn't contain k: @@ -92,7 +92,7 @@ enum RemoveFrom { /// /// Practically, this means that for each commit block turning from recent to ancient we do the /// following: -/// ``` +/// ```text /// is_canonical: /// inserts: Ignored (left alone in the backing database). /// deletes: Enacted; however, recent history queue is checked for ongoing references. This is @@ -263,7 +263,7 @@ impl EarlyMergeDB { let mut refs = HashMap::new(); let mut latest_era = None; if let Some(val) = db.get(col, &LATEST_ERA_KEY).expect("Low-level database error.") { - let mut era = decode::(&val); + let mut era = decode::(&val).expect("decoding db value failed"); latest_era = Some(era); loop { let mut db_key = DatabaseKey { @@ -394,7 +394,6 @@ impl JournalDB for EarlyMergeDB { .filter_map(|(k, (v, r))| if r > 0 { assert!(r == 1); Some((k, v)) } else { assert!(r >= -1); None }) .collect(); - // TODO: check all removes are in the db. // Process the new inserts. diff --git a/util/journaldb/src/lib.rs b/util/journaldb/src/lib.rs index c1fb23b6cd15eff9e685d40cbded05016772fc3b..7607271c8d2486661e6ca9e33d9a1de42e1c52c1 100644 --- a/util/journaldb/src/lib.rs +++ b/util/journaldb/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/util/journaldb/src/overlaydb.rs b/util/journaldb/src/overlaydb.rs index 6ff1b97e8be42ce4221968b19572f1b075ee6bf6..46bf42c0ad577ff65ce43d58455670371a25ab7c 100644 --- a/util/journaldb/src/overlaydb.rs +++ b/util/journaldb/src/overlaydb.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -21,7 +21,7 @@ use std::collections::HashMap; use std::collections::hash_map::Entry; use error::{Result, BaseDataError}; use ethereum_types::H256; -use rlp::{UntrustedRlp, RlpStream, Encodable, DecoderError, Decodable, encode, decode}; +use rlp::{Rlp, RlpStream, Encodable, DecoderError, Decodable, encode, decode}; use hashdb::*; use memorydb::*; use kvdb::{KeyValueDB, DBTransaction}; @@ -64,7 +64,7 @@ impl Encodable for Payload { } impl Decodable for Payload { - fn decode(rlp: &UntrustedRlp) -> ::std::result::Result { + fn decode(rlp: &Rlp) -> ::std::result::Result { let payload = Payload { count: rlp.val_at(0)?, value: DBValue::from_slice(rlp.at(1)?.data()?), @@ -137,7 +137,7 @@ impl OverlayDB { fn payload(&self, key: &H256) -> Option { self.backing.get(self.column, key) .expect("Low-level database error. Some issue with your hard disk?") - .map(|d| decode(&d)) + .map(|d| decode(&d).expect("decoding db value failed")) } /// Put the refs and value of the given key, possibly deleting it from the db. diff --git a/util/journaldb/src/overlayrecentdb.rs b/util/journaldb/src/overlayrecentdb.rs index 9ac8a4e294f2e9c1c5554e545774e46257c9549b..c7153b889d4c647c168002c0e438fefab34f745a 100644 --- a/util/journaldb/src/overlayrecentdb.rs +++ b/util/journaldb/src/overlayrecentdb.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -21,7 +21,7 @@ use std::collections::hash_map::Entry; use std::sync::Arc; use parking_lot::RwLock; use heapsize::HeapSizeOf; -use rlp::{UntrustedRlp, RlpStream, encode, decode, DecoderError, Decodable, Encodable}; +use rlp::{Rlp, RlpStream, encode, decode, DecoderError, Decodable, Encodable}; use hashdb::*; use memorydb::*; use super::{DB_PREFIX_LEN, LATEST_ERA_KEY}; @@ -78,7 +78,7 @@ struct DatabaseValue { } impl Decodable for DatabaseValue { - fn decode(rlp: &UntrustedRlp) -> Result { + fn decode(rlp: &Rlp) -> Result { let id = rlp.val_at(0)?; let inserts = rlp.at(1)?.iter().map(|r| { let k = r.val_at(0)?; @@ -186,7 +186,7 @@ impl OverlayRecentDB { let mut earliest_era = None; let mut cumulative_size = 0; if let Some(val) = db.get(col, &LATEST_ERA_KEY).expect("Low-level database error.") { - let mut era = decode::(&val); + let mut era = decode::(&val).expect("decoding db value failed"); latest_era = Some(era); loop { let mut db_key = DatabaseKey { @@ -195,7 +195,7 @@ impl OverlayRecentDB { }; while let Some(rlp_data) = db.get(col, &encode(&db_key)).expect("Low-level database error.") { trace!("read_overlay: era={}, index={}", era, db_key.index); - let value = decode::(&rlp_data); + let value = decode::(&rlp_data).expect(&format!("read_overlay: Error decoding DatabaseValue era={}, index{}", era, db_key.index)); count += value.inserts.len(); let mut inserted_keys = Vec::new(); for (k, v) in value.inserts { diff --git a/util/journaldb/src/refcounteddb.rs b/util/journaldb/src/refcounteddb.rs index bf366faf75311c97c7b7246113700eec86b2558b..cc81bbfba4dea1e14689dbdd0c3752e895e770a3 100644 --- a/util/journaldb/src/refcounteddb.rs +++ b/util/journaldb/src/refcounteddb.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -40,7 +40,7 @@ use util::{DatabaseKey, DatabaseValueView, DatabaseValueRef}; /// the removals actually take effect. /// /// journal format: -/// ``` +/// ```text /// [era, 0] => [ id, [insert_0, ...], [remove_0, ...] ] /// [era, 1] => [ id, [insert_0, ...], [remove_0, ...] ] /// [era, n] => [ ... ] @@ -62,17 +62,18 @@ pub struct RefCountedDB { impl RefCountedDB { /// Create a new instance given a `backing` database. - pub fn new(backing: Arc, col: Option) -> RefCountedDB { - let latest_era = backing.get(col, &LATEST_ERA_KEY).expect("Low-level database error.") - .map(|val| decode::(&val)); + pub fn new(backing: Arc, column: Option) -> RefCountedDB { + let latest_era = backing.get(column, &LATEST_ERA_KEY) + .expect("Low-level database error.") + .map(|v| decode::(&v).expect("decoding db value failed")); RefCountedDB { - forward: OverlayDB::new(backing.clone(), col), - backing: backing, + forward: OverlayDB::new(backing.clone(), column), + backing, inserts: vec![], removes: vec![], - latest_era: latest_era, - column: col, + latest_era, + column, } } } diff --git a/util/journaldb/src/traits.rs b/util/journaldb/src/traits.rs index aaf5b27970141962d0cfc16142f95d94273e3f5c..e37ac8aabf58268086e98db81a58b0c8086a3a93 100644 --- a/util/journaldb/src/traits.rs +++ b/util/journaldb/src/traits.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/util/journaldb/src/util.rs b/util/journaldb/src/util.rs index 52dbad7e1daad66cc28616243170486a451ba7f4..e99be458e58d762208d749ccb76522fda79949b6 100644 --- a/util/journaldb/src/util.rs +++ b/util/journaldb/src/util.rs @@ -15,7 +15,7 @@ // along with Parity. If not, see . use ethereum_types::H256; -use rlp::{RlpStream, Encodable, UntrustedRlp, DecoderError}; +use rlp::{RlpStream, Encodable, Rlp, DecoderError}; const PADDING : [u8; 10] = [ 0u8; 10 ]; @@ -34,13 +34,13 @@ impl Encodable for DatabaseKey { } pub struct DatabaseValueView<'a> { - rlp: UntrustedRlp<'a>, + rlp: Rlp<'a>, } impl<'a> DatabaseValueView<'a> { pub fn from_rlp(data: &'a [u8]) -> Self { DatabaseValueView { - rlp: UntrustedRlp::new(data), + rlp: Rlp::new(data), } } diff --git a/util/kvdb-memorydb/src/lib.rs b/util/kvdb-memorydb/src/lib.rs index 0530c613e3185e95268469859a5fddb3edd386bd..45ed1c3e69f1160c105acbf7ffa743717da72184 100644 --- a/util/kvdb-memorydb/src/lib.rs +++ b/util/kvdb-memorydb/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/util/kvdb-rocksdb/Cargo.toml b/util/kvdb-rocksdb/Cargo.toml index 89a2f6403faf44a1422b8a49bf5e7b53c2ffe573..07016b68a2de7feabec8f3c729ec35434f1a70e6 100644 --- a/util/kvdb-rocksdb/Cargo.toml +++ b/util/kvdb-rocksdb/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Parity Technologies "] [dependencies] elastic-array = "0.10" -ethereum-types = "0.2" +ethereum-types = "0.3" kvdb = { path = "../kvdb" } log = "0.3" num_cpus = "1.0" diff --git a/util/kvdb-rocksdb/src/lib.rs b/util/kvdb-rocksdb/src/lib.rs index 4f2220a11dd179637bbaba13db7b86e2580c8018..605246829842ae6da7971d2bcc4ae2594e7c7b59 100644 --- a/util/kvdb-rocksdb/src/lib.rs +++ b/util/kvdb-rocksdb/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -388,7 +388,6 @@ impl Database { DBTransaction::new() } - fn to_overlay_column(col: Option) -> usize { col.map_or(0, |c| (c + 1) as usize) } diff --git a/util/kvdb/src/lib.rs b/util/kvdb/src/lib.rs index 6a8412c0e8c32fd7a2f12e3d0d9eae782683d9bc..78e7b2dc192d777c417110a7e2de080fedf45afc 100644 --- a/util/kvdb/src/lib.rs +++ b/util/kvdb/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -22,6 +22,8 @@ extern crate elastic_array; extern crate ethcore_bytes as bytes; use std::io; +use std::path::Path; +use std::sync::Arc; use elastic_array::{ElasticArray128, ElasticArray32}; use bytes::Bytes; @@ -176,3 +178,10 @@ pub trait KeyValueDB: Sync + Send { /// Attempt to replace this database with a new one located at the given path. fn restore(&self, new_db: &str) -> Result<()>; } + +/// Generic key-value database handler. This trait contains one function `open`. When called, it opens database with a +/// predefined config. +pub trait KeyValueDBHandler: Send + Sync { + /// Open the predefined key-value database. + fn open(&self, path: &Path) -> Result>; +} diff --git a/util/macros/src/lib.rs b/util/macros/src/lib.rs index 78bcd0397efb33b959c94808568b7171e2af3777..cc5f92ba1506e91f99b6896cb17da4170666cf88 100644 --- a/util/macros/src/lib.rs +++ b/util/macros/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/util/table/Cargo.toml b/util/mem/Cargo.toml similarity index 86% rename from util/table/Cargo.toml rename to util/mem/Cargo.toml index f2faca184dc6722881569997e34ae30c737b6fbe..1cca222142e1014a3a6cc678ee66b2dc0ff2e016 100644 --- a/util/table/Cargo.toml +++ b/util/mem/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "table" +name = "mem" version = "0.1.0" authors = ["Parity Technologies "] diff --git a/util/mem/src/lib.rs b/util/mem/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..db3ad592398079ea2f25d9f146b0986c2fc70634 --- /dev/null +++ b/util/mem/src/lib.rs @@ -0,0 +1,56 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use std::ops::{Deref, DerefMut}; +use std::ptr; + +/// Wrapper to zero out memory when dropped. +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Memzero> { + mem: T, +} + +impl> From for Memzero { + fn from(mem: T) -> Memzero { + Memzero { mem } + } +} + +impl> Drop for Memzero { + fn drop(&mut self) { + let n = self.mem.as_mut().len(); + let p = self.mem.as_mut().as_mut_ptr(); + for i in 0..n { + unsafe { + ptr::write_volatile(p.offset(i as isize), 0) + } + } + } +} + +impl> Deref for Memzero { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.mem + } +} + +impl> DerefMut for Memzero { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.mem + } +} diff --git a/util/memory_cache/src/lib.rs b/util/memory_cache/src/lib.rs index af70b0cff30eb59abdb5dccca97caf21a7960ea8..ff996142b925a30aa56a9a1587e18e2a7daf1306 100644 --- a/util/memory_cache/src/lib.rs +++ b/util/memory_cache/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/util/memorydb/Cargo.toml b/util/memorydb/Cargo.toml index e5feabe5fb7d22fdc3b97ebc3dd5074739e87f51..41c41bb628eb7acfa6e97adcd6c5c5cbbcdca5cf 100644 --- a/util/memorydb/Cargo.toml +++ b/util/memorydb/Cargo.toml @@ -6,10 +6,9 @@ description = "in-memory implementation of hashdb" license = "GPL-3.0" [dependencies] -bigint = "4.0" elastic-array = "0.10" heapsize = "0.4" -ethereum-types = "0.2" +ethereum-types = "0.3" keccak-hash = { version = "0.1.0", path = "../hash" } hashdb = { version = "0.1.1", path = "../hashdb" } plain_hasher = { path = "../plain_hasher" } diff --git a/util/memorydb/src/lib.rs b/util/memorydb/src/lib.rs index 12eb62e0574aec38f0f09b06e7bec5b20f28d599..e297d1e6d1a8408e429b03775dd9f77962fafab1 100644 --- a/util/memorydb/src/lib.rs +++ b/util/memorydb/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/util/migration/Cargo.toml b/util/migration-rocksdb/Cargo.toml similarity index 91% rename from util/migration/Cargo.toml rename to util/migration-rocksdb/Cargo.toml index d938822bacdff21a0b8678c45db3068aa202b3d3..3f0b8e75204aae7494c6fd871e6f7f03746178bc 100644 --- a/util/migration/Cargo.toml +++ b/util/migration-rocksdb/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "migration" +name = "migration-rocksdb" version = "0.1.0" authors = ["Parity Technologies "] diff --git a/util/migration/src/lib.rs b/util/migration-rocksdb/src/lib.rs similarity index 99% rename from util/migration/src/lib.rs rename to util/migration-rocksdb/src/lib.rs index fbc9681b40e7249a2a5e6b25add3d556e012edf5..2e39a380baae750a031b37d772e6a56ba2eabb67 100644 --- a/util/migration/src/lib.rs +++ b/util/migration-rocksdb/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/util/migration/tests/tests.rs b/util/migration-rocksdb/tests/tests.rs similarity index 98% rename from util/migration/tests/tests.rs rename to util/migration-rocksdb/tests/tests.rs index c1ff8228f87a7767d1af107eee13716125ffd460..c98ff9d71b1db67276ee235e16a384b8b24f4026 100644 --- a/util/migration/tests/tests.rs +++ b/util/migration-rocksdb/tests/tests.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -22,7 +22,7 @@ extern crate macros; extern crate tempdir; extern crate kvdb_rocksdb; -extern crate migration; +extern crate migration_rocksdb as migration; use std::collections::BTreeMap; use std::path::{Path, PathBuf}; @@ -119,7 +119,6 @@ impl Migration for AddsColumn { batch.insert(key.into_vec(), value.into_vec(), dest)?; } - if col == Some(1) { batch.insert(vec![1, 2, 3], vec![4, 5, 6], dest)?; } diff --git a/util/network-devp2p/Cargo.toml b/util/network-devp2p/Cargo.toml index 7d86ce7a073333ace7b366f0954b61fb08fa3828..4a5d2d942ec8c6c133bea1e9cbc8f05886839942 100644 --- a/util/network-devp2p/Cargo.toml +++ b/util/network-devp2p/Cargo.toml @@ -3,7 +3,7 @@ description = "DevP2P implementation of the ethcore network library" homepage = "http://parity.io" license = "GPL-3.0" name = "ethcore-network-devp2p" -version = "1.11.0" +version = "1.12.0" authors = ["Parity Technologies "] [dependencies] @@ -11,23 +11,23 @@ log = "0.3" mio = "0.6.8" bytes = "0.4" rand = "0.4" -tiny-keccak = "1.3" +tiny-keccak = "1.4" rust-crypto = "0.2.34" slab = "0.2" -igd = "0.6" +igd = "0.7" libc = "0.2.7" parking_lot = "0.5" ansi_term = "0.10" rustc-hex = "1.0" -ethcore-io = { path = "../io" } +ethcore-io = { path = "../io", features = ["mio"] } ethcore-bytes = { path = "../bytes" } +ethcore-crypto = { path = "../../ethcore/crypto" } +ethcore-logger = { path ="../../logger" } ethcore-network = { path = "../network" } -ethereum-types = "0.2" +ethereum-types = "0.3" ethkey = { path = "../../ethkey" } -ethcrypto = { path = "../../ethcrypto" } rlp = { path = "../rlp" } path = { path = "../path" } -ethcore-logger = { path ="../../logger" } ipnetwork = "0.12.6" keccak-hash = { path = "../hash" } snappy = { git = "https://github.com/paritytech/rust-snappy" } @@ -38,6 +38,7 @@ error-chain = { version = "0.11", default-features = false } [dev-dependencies] tempdir = "0.3" +assert_matches = "1.2" [features] default = [] diff --git a/util/network-devp2p/src/connection.rs b/util/network-devp2p/src/connection.rs index e32439756fdfff13788bb599d9594e317d9dcabc..37824ae5d758545db40f8bb13941523069559a43 100644 --- a/util/network-devp2p/src/connection.rs +++ b/util/network-devp2p/src/connection.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -14,32 +14,31 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use std::sync::Arc; use std::collections::VecDeque; use std::net::SocketAddr; use std::sync::atomic::{AtomicBool, Ordering as AtomicOrdering}; +use std::time::Duration; use hash::{keccak, write_keccak}; use mio::{Token, Ready, PollOpt}; use mio::deprecated::{Handler, EventLoop, TryRead, TryWrite}; use mio::tcp::*; use ethereum_types::{H128, H256, H512}; use ethcore_bytes::*; -use rlp::*; +use rlp::{Rlp, RlpStream}; use std::io::{self, Cursor, Read, Write}; use io::{IoContext, StreamToken}; use handshake::Handshake; -use stats::NetworkStats; use rcrypto::blockmodes::*; use rcrypto::aessafe::*; use rcrypto::symmetriccipher::*; use rcrypto::buffer::*; use tiny_keccak::Keccak; use bytes::{Buf, BufMut}; -use crypto; +use ethkey::crypto; use network::{Error, ErrorKind}; const ENCRYPTED_HEADER_LEN: usize = 32; -const RECIEVE_PAYLOAD_TIMEOUT: u64 = 30000; +const RECEIVE_PAYLOAD: Duration = Duration::from_secs(30); pub const MAX_PAYLOAD_SIZE: usize = (1 << 24) - 1; pub trait GenericSocket : Read + Write { @@ -61,8 +60,6 @@ pub struct GenericConnection { send_queue: VecDeque>, /// Event flags this connection expects interest: Ready, - /// Shared network statistics - stats: Arc, /// Registered flag registered: AtomicBool, } @@ -87,7 +84,6 @@ impl GenericConnection { match sock_ref.take(max as u64).try_read(unsafe { self.rec_buf.bytes_mut() }) { Ok(Some(size)) if size != 0 => { unsafe { self.rec_buf.advance_mut(size); } - self.stats.inc_recv(size); trace!(target:"network", "{}: Read {} of {} bytes", self.token, self.rec_buf.len(), self.rec_size); if self.rec_size != 0 && self.rec_buf.len() == self.rec_size { self.rec_size = 0; @@ -141,11 +137,9 @@ impl GenericConnection { match self.socket.try_write(Buf::bytes(&buf)) { Ok(Some(size)) if (pos + size) < send_size => { buf.advance(size); - self.stats.inc_send(size); Ok(WriteStatus::Ongoing) }, Ok(Some(size)) if (pos + size) == send_size => { - self.stats.inc_send(size); trace!(target:"network", "{}: Wrote {} bytes", self.token, send_size); Ok(WriteStatus::Complete) }, @@ -171,7 +165,7 @@ pub type Connection = GenericConnection; impl Connection { /// Create a new connection with given id and socket. - pub fn new(token: StreamToken, socket: TcpStream, stats: Arc) -> Connection { + pub fn new(token: StreamToken, socket: TcpStream) -> Connection { Connection { token: token, socket: socket, @@ -179,7 +173,6 @@ impl Connection { rec_buf: Bytes::new(), rec_size: 0, interest: Ready::hup() | Ready::readable(), - stats: stats, registered: AtomicBool::new(false), } } @@ -213,7 +206,6 @@ impl Connection { rec_size: 0, send_queue: self.send_queue.clone(), interest: Ready::hup(), - stats: self.stats.clone(), registered: AtomicBool::new(false), }) } @@ -399,7 +391,7 @@ impl EncryptedConnection { self.decoder.decrypt(&mut RefReadBuffer::new(&header[0..16]), &mut RefWriteBuffer::new(&mut hdec), false).expect("Invalid length or padding"); let length = ((((hdec[0] as u32) << 8) + (hdec[1] as u32)) << 8) + (hdec[2] as u32); - let header_rlp = UntrustedRlp::new(&hdec[3..6]); + let header_rlp = Rlp::new(&hdec[3..6]); let protocol_id = header_rlp.val_at::(0)?; self.payload_len = length as usize; @@ -456,7 +448,7 @@ impl EncryptedConnection { if let EncryptedConnectionState::Header = self.read_state { if let Some(data) = self.connection.readable()? { self.read_header(&data)?; - io.register_timer(self.connection.token, RECIEVE_PAYLOAD_TIMEOUT)?; + io.register_timer(self.connection.token, RECEIVE_PAYLOAD)?; } }; if let EncryptedConnectionState::Payload = self.read_state { @@ -507,13 +499,11 @@ mod tests { use std::cmp; use std::collections::VecDeque; use std::io::{Read, Write, Cursor, ErrorKind, Result, Error}; - use std::sync::Arc; use std::sync::atomic::AtomicBool; use mio::{Ready}; use ethcore_bytes::Bytes; use io::*; - use super::super::stats::*; use super::*; pub struct TestSocket { @@ -625,7 +615,6 @@ mod tests { rec_buf: Bytes::new(), rec_size: 0, interest: Ready::hup() | Ready::readable(), - stats: Arc::::new(NetworkStats::new()), registered: AtomicBool::new(false), } } @@ -648,7 +637,6 @@ mod tests { rec_buf: Bytes::new(), rec_size: 0, interest: Ready::hup() | Ready::readable(), - stats: Arc::::new(NetworkStats::new()), registered: AtomicBool::new(false), } } diff --git a/util/network-devp2p/src/discovery.rs b/util/network-devp2p/src/discovery.rs index 4a43ab3eb62ff968e1bf94cf3cf45ae6030eaa40..5f8f0cdbc2a2caf2858f63146c0e8b6ca0da15a9 100644 --- a/util/network-devp2p/src/discovery.rs +++ b/util/network-devp2p/src/discovery.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -16,38 +16,32 @@ use ethcore_bytes::Bytes; use std::net::SocketAddr; -use std::collections::{HashSet, HashMap, BTreeMap, VecDeque}; -use std::mem; +use std::collections::{HashSet, HashMap, VecDeque}; use std::default::Default; use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; -use mio::*; -use mio::deprecated::{Handler, EventLoop}; -use mio::udp::*; use hash::keccak; use ethereum_types::{H256, H520}; -use rlp::*; +use rlp::{Rlp, RlpStream, encode_list}; use node_table::*; use network::{Error, ErrorKind}; -use io::{StreamToken, IoContext}; use ethkey::{Secret, KeyPair, sign, recover}; use network::IpFilter; use PROTOCOL_VERSION; -const ADDRESS_BYTES_SIZE: u32 = 32; // Size of address type in bytes. -const ADDRESS_BITS: u32 = 8 * ADDRESS_BYTES_SIZE; // Denoted by n in [Kademlia]. -const NODE_BINS: u32 = ADDRESS_BITS - 1; // Size of m_state (excludes root, which is us). +const ADDRESS_BYTES_SIZE: usize = 32; // Size of address type in bytes. +const ADDRESS_BITS: usize = 8 * ADDRESS_BYTES_SIZE; // Denoted by n in [Kademlia]. const DISCOVERY_MAX_STEPS: u16 = 8; // Max iterations of discovery. (discover) const BUCKET_SIZE: usize = 16; // Denoted by k in [Kademlia]. Number of nodes stored in each bucket. const ALPHA: usize = 3; // Denoted by \alpha in [Kademlia]. Number of concurrent FindNode requests. -const MAX_DATAGRAM_SIZE: usize = 1280; +pub const MAX_DATAGRAM_SIZE: usize = 1280; const PACKET_PING: u8 = 1; const PACKET_PONG: u8 = 2; const PACKET_FIND_NODE: u8 = 3; const PACKET_NEIGHBOURS: u8 = 4; -const PING_TIMEOUT_MS: u64 = 300; +const PING_TIMEOUT: Duration = Duration::from_millis(300); const MAX_NODES_PING: usize = 32; // Max nodes to add/ping at once #[derive(Clone, Debug)] @@ -80,9 +74,9 @@ impl NodeBucket { } } -struct Datagramm { - payload: Bytes, - address: SocketAddr, +pub struct Datagram { + pub payload: Bytes, + pub address: SocketAddr, } pub struct Discovery { @@ -90,13 +84,11 @@ pub struct Discovery { id_hash: H256, secret: Secret, public_endpoint: NodeEndpoint, - udp_socket: UdpSocket, - token: StreamToken, discovery_round: u16, discovery_id: NodeId, discovery_nodes: HashSet, node_buckets: Vec, - send_queue: VecDeque, + send_queue: VecDeque, check_timestamps: bool, adding_nodes: Vec, ip_filter: IpFilter, @@ -108,19 +100,16 @@ pub struct TableUpdates { } impl Discovery { - pub fn new(key: &KeyPair, listen: SocketAddr, public: NodeEndpoint, token: StreamToken, ip_filter: IpFilter) -> Discovery { - let socket = UdpSocket::bind(&listen).expect("Error binding UDP socket"); + pub fn new(key: &KeyPair, public: NodeEndpoint, ip_filter: IpFilter) -> Discovery { Discovery { id: key.public().clone(), id_hash: keccak(key.public()), secret: key.secret().clone(), public_endpoint: public, - token: token, discovery_round: 0, discovery_id: NodeId::new(), discovery_nodes: HashSet::new(), - node_buckets: (0..NODE_BINS).map(|_| NodeBucket::new()).collect(), - udp_socket: socket, + node_buckets: (0..ADDRESS_BITS).map(|_| NodeBucket::new()).collect(), send_queue: VecDeque::new(), check_timestamps: true, adding_nodes: Vec::new(), @@ -155,8 +144,16 @@ impl Discovery { fn update_node(&mut self, e: NodeEntry) { trace!(target: "discovery", "Inserting {:?}", &e); let id_hash = keccak(e.id); + let dist = match Discovery::distance(&self.id_hash, &id_hash) { + Some(dist) => dist, + None => { + debug!(target: "discovery", "Attempted to update own entry: {:?}", e); + return; + } + }; + let ping = { - let bucket = &mut self.node_buckets[Discovery::distance(&self.id_hash, &id_hash) as usize]; + let bucket = &mut self.node_buckets[dist]; let updated = if let Some(node) = bucket.nodes.iter_mut().find(|n| n.address.id == e.id) { node.address = e.clone(); node.timeout = None; @@ -181,7 +178,15 @@ impl Discovery { /// Removes the timeout of a given NodeId if it can be found in one of the discovery buckets fn clear_ping(&mut self, id: &NodeId) { - let bucket = &mut self.node_buckets[Discovery::distance(&self.id_hash, &keccak(id)) as usize]; + let dist = match Discovery::distance(&self.id_hash, &keccak(id)) { + Some(dist) => dist, + None => { + debug!(target: "discovery", "Received ping from self"); + return + } + }; + + let bucket = &mut self.node_buckets[dist]; if let Some(node) = bucket.nodes.iter_mut().find(|n| &n.address.id == id) { node.timeout = None; } @@ -212,11 +217,12 @@ impl Discovery { trace!(target: "discovery", "Starting round {:?}", self.discovery_round); let mut tried_count = 0; { - let nearest = Discovery::nearest_node_entries(&self.discovery_id, &self.node_buckets).into_iter(); + let nearest = self.nearest_node_entries(&self.discovery_id).into_iter(); let nearest = nearest.filter(|x| !self.discovery_nodes.contains(&x.id)).take(ALPHA).collect::>(); for r in nearest { let rlp = encode_list(&(&[self.discovery_id.clone()][..])); - self.send_packet(PACKET_FIND_NODE, &r.endpoint.udp_address(), &rlp); + self.send_packet(PACKET_FIND_NODE, &r.endpoint.udp_address(), &rlp) + .unwrap_or_else(|e| warn!("Error sending node discovery packet for {:?}: {:?}", &r.endpoint, e)); self.discovery_nodes.insert(r.id.clone()); tried_count += 1; trace!(target: "discovery", "Sent FindNode to {:?}", &r.endpoint); @@ -232,17 +238,17 @@ impl Discovery { self.discovery_round += 1; } - fn distance(a: &H256, b: &H256) -> u32 { - let d = *a ^ *b; - let mut ret:u32 = 0; - for i in 0..32 { - let mut v: u8 = d[i]; - while v != 0 { - v >>= 1; - ret += 1; + /// The base 2 log of the distance between a and b using the XOR metric. + fn distance(a: &H256, b: &H256) -> Option { + for i in (0..ADDRESS_BYTES_SIZE).rev() { + let byte_index = ADDRESS_BYTES_SIZE - i - 1; + let d: u8 = a[byte_index] ^ b[byte_index]; + if d != 0 { + let high_bit_index = 7 - d.leading_zeros() as usize; + return Some(i * 8 + high_bit_index); } } - ret + None // a and b are equal, so log distance is -inf } fn ping(&mut self, node: &NodeEndpoint) { @@ -251,16 +257,17 @@ impl Discovery { self.public_endpoint.to_rlp_list(&mut rlp); node.to_rlp_list(&mut rlp); trace!(target: "discovery", "Sent Ping to {:?}", &node); - self.send_packet(PACKET_PING, &node.udp_address(), &rlp.drain()); + self.send_packet(PACKET_PING, &node.udp_address(), &rlp.drain()) + .unwrap_or_else(|e| warn!("Error sending Ping packet: {:?}", e)) } - fn send_packet(&mut self, packet_id: u8, address: &SocketAddr, payload: &[u8]) { + fn send_packet(&mut self, packet_id: u8, address: &SocketAddr, payload: &[u8]) -> Result<(), Error> { let mut rlp = RlpStream::new(); rlp.append_raw(&[packet_id], 1); let source = Rlp::new(payload); - rlp.begin_list(source.item_count() + 1); - for i in 0 .. source.item_count() { - rlp.append_raw(source.at(i).as_raw(), 1); + rlp.begin_list(source.item_count()? + 1); + for i in 0 .. source.item_count()? { + rlp.append_raw(source.at(i)?.as_raw(), 1); } let timestamp = 60 + SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs() as u32; rlp.append(×tamp); @@ -269,9 +276,9 @@ impl Discovery { let hash = keccak(bytes.as_ref()); let signature = match sign(&self.secret, &hash) { Ok(s) => s, - Err(_) => { + Err(e) => { warn!("Error signing UDP packet"); - return; + return Err(Error::from(e)); } }; let mut packet = Bytes::with_capacity(bytes.len() + 32 + 65); @@ -281,89 +288,66 @@ impl Discovery { let signed_hash = keccak(&packet[32..]); packet[0..32].clone_from_slice(&signed_hash); self.send_to(packet, address.clone()); + Ok(()) } - fn nearest_node_entries(target: &NodeId, buckets: &[NodeBucket]) -> Vec { - let mut found: BTreeMap> = BTreeMap::new(); - let mut count = 0; + fn nearest_node_entries(&self, target: &NodeId) -> Vec { let target_hash = keccak(target); + let target_distance = self.id_hash ^ target_hash; + + let mut ret = Vec::::with_capacity(BUCKET_SIZE); + + // Sort bucket entries by distance to target and append to end of result vector. + let append_bucket = |results: &mut Vec, bucket: &NodeBucket| -> bool { + let mut sorted_entries: Vec<&BucketEntry> = bucket.nodes.iter().collect(); + sorted_entries.sort_unstable_by_key(|entry| entry.id_hash ^ target_hash); + + let remaining_capacity = results.capacity() - results.len(); + let to_append = if remaining_capacity < sorted_entries.len() { + &sorted_entries[0..remaining_capacity] + } else { + &sorted_entries + }; + for entry in to_append.iter() { + results.push(entry.address.clone()); + } + results.len() == results.capacity() + }; - // Sort nodes by distance to target - for bucket in buckets { - for node in &bucket.nodes { - let distance = Discovery::distance(&target_hash, &node.id_hash); - found.entry(distance).or_insert_with(Vec::new).push(&node.address); - if count == BUCKET_SIZE { - // delete the most distant element - let remove = { - let (key, last) = found.iter_mut().next_back().expect("Last element is always Some when count > 0"); - last.pop(); - if last.is_empty() { Some(key.clone()) } else { None } - }; - if let Some(remove) = remove { - found.remove(&remove); - } - } - else { - count += 1; + // This algorithm leverages the structure of the routing table to efficiently find the + // nearest entries to a target hash. First, we compute the XOR distance from this node to + // the target. On a first pass, we iterate from the MSB of the distance, stopping at any + // buckets where the distance bit is set, and skipping the buckets where it is unset. These + // must be in order the nearest to the target. On a second pass, we traverse from LSB to + // MSB, appending the buckets skipped on the first pass. The reason this works is that all + // entries in bucket i have a common prefix of length exactly 32 - i - 1 with the ID of this + // node. + + for i in 0..ADDRESS_BITS { + if ((target_distance[i / 8] << (i % 8)) & 0x80) != 0 { + let bucket = &self.node_buckets[ADDRESS_BITS - i - 1]; + if !bucket.nodes.is_empty() && append_bucket(&mut ret, bucket) { + return ret; } } } - - let mut ret:Vec = Vec::new(); - for nodes in found.values() { - ret.extend(nodes.iter().map(|&n| n.clone())); - } - ret - } - - pub fn writable(&mut self, io: &IoContext) where Message: Send + Sync + Clone { - while let Some(data) = self.send_queue.pop_front() { - match self.udp_socket.send_to(&data.payload, &data.address) { - Ok(Some(size)) if size == data.payload.len() => { - }, - Ok(Some(_)) => { - warn!("UDP sent incomplete datagramm"); - }, - Ok(None) => { - self.send_queue.push_front(data); - return; - } - Err(e) => { - debug!("UDP send error: {:?}, address: {:?}", e, &data.address); - return; + for i in (0..ADDRESS_BITS).rev() { + if ((target_distance[i / 8] << (i % 8)) & 0x80) == 0 { + let bucket = &self.node_buckets[ADDRESS_BITS - i - 1]; + if !bucket.nodes.is_empty() && append_bucket(&mut ret, bucket) { + return ret; } } } - io.update_registration(self.token).unwrap_or_else(|e| debug!("Error updating discovery registration: {:?}", e)); + ret } fn send_to(&mut self, payload: Bytes, address: SocketAddr) { - self.send_queue.push_back(Datagramm { payload: payload, address: address }); - } - - pub fn readable(&mut self, io: &IoContext) -> Option where Message: Send + Sync + Clone { - let mut buf: [u8; MAX_DATAGRAM_SIZE] = unsafe { mem::uninitialized() }; - let writable = !self.send_queue.is_empty(); - let res = match self.udp_socket.recv_from(&mut buf) { - Ok(Some((len, address))) => self.on_packet(&buf[0..len], address).unwrap_or_else(|e| { - debug!("Error processing UDP packet: {:?}", e); - None - }), - Ok(_) => None, - Err(e) => { - debug!("Error reading UPD socket: {:?}", e); - None - } - }; - let new_writable = !self.send_queue.is_empty(); - if writable != new_writable { - io.update_registration(self.token).unwrap_or_else(|e| debug!("Error updating discovery registration: {:?}", e)); - } - res + self.send_queue.push_back(Datagram { payload: payload, address: address }); } - fn on_packet(&mut self, packet: &[u8], from: SocketAddr) -> Result, Error> { + + pub fn on_packet(&mut self, packet: &[u8], from: SocketAddr) -> Result, Error> { // validate packet if packet.len() < 32 + 65 + 4 + 1 { return Err(ErrorKind::BadProtocol.into()); @@ -379,14 +363,14 @@ impl Discovery { let node_id = recover(&signature.into(), &keccak(signed))?; let packet_id = signed[0]; - let rlp = UntrustedRlp::new(&signed[1..]); + let rlp = Rlp::new(&signed[1..]); match packet_id { PACKET_PING => self.on_ping(&rlp, &node_id, &from, &hash_signed), PACKET_PONG => self.on_pong(&rlp, &node_id, &from), PACKET_FIND_NODE => self.on_find_node(&rlp, &node_id, &from), PACKET_NEIGHBOURS => self.on_neighbours(&rlp, &node_id, &from), _ => { - debug!("Unknown UDP packet: {}", packet_id); + debug!(target: "discovery", "Unknown UDP packet: {}", packet_id); Ok(None) } } @@ -406,7 +390,7 @@ impl Discovery { entry.endpoint.is_allowed(&self.ip_filter) && entry.id != self.id } - fn on_ping(&mut self, rlp: &UntrustedRlp, node: &NodeId, from: &SocketAddr, echo_hash: &[u8]) -> Result, Error> { + fn on_ping(&mut self, rlp: &Rlp, node: &NodeId, from: &SocketAddr, echo_hash: &[u8]) -> Result, Error> { trace!(target: "discovery", "Got Ping from {:?}", &from); let source = NodeEndpoint::from_rlp(&rlp.at(1)?)?; let dest = NodeEndpoint::from_rlp(&rlp.at(2)?)?; @@ -425,12 +409,12 @@ impl Discovery { let mut response = RlpStream::new_list(2); dest.to_rlp_list(&mut response); response.append(&echo_hash); - self.send_packet(PACKET_PONG, from, &response.drain()); + self.send_packet(PACKET_PONG, from, &response.drain())?; Ok(Some(TableUpdates { added: added_map, removed: HashSet::new() })) } - fn on_pong(&mut self, rlp: &UntrustedRlp, node: &NodeId, from: &SocketAddr) -> Result, Error> { + fn on_pong(&mut self, rlp: &Rlp, node: &NodeId, from: &SocketAddr) -> Result, Error> { trace!(target: "discovery", "Got Pong from {:?}", &from); // TODO: validate pong packet in rlp.val_at(1) let dest = NodeEndpoint::from_rlp(&rlp.at(0)?)?; @@ -445,18 +429,18 @@ impl Discovery { Ok(None) } - fn on_find_node(&mut self, rlp: &UntrustedRlp, _node: &NodeId, from: &SocketAddr) -> Result, Error> { + fn on_find_node(&mut self, rlp: &Rlp, _node: &NodeId, from: &SocketAddr) -> Result, Error> { trace!(target: "discovery", "Got FindNode from {:?}", &from); let target: NodeId = rlp.val_at(0)?; let timestamp: u64 = rlp.val_at(1)?; self.check_timestamp(timestamp)?; - let nearest = Discovery::nearest_node_entries(&target, &self.node_buckets); + let nearest = self.nearest_node_entries(&target); if nearest.is_empty() { return Ok(None); } let mut packets = Discovery::prepare_neighbours_packets(&nearest); for p in packets.drain(..) { - self.send_packet(PACKET_NEIGHBOURS, from, &p); + self.send_packet(PACKET_NEIGHBOURS, from, &p)?; } trace!(target: "discovery", "Sent {} Neighbours to {:?}", nearest.len(), &from); Ok(None) @@ -478,7 +462,7 @@ impl Discovery { packets.collect() } - fn on_neighbours(&mut self, rlp: &UntrustedRlp, _node: &NodeId, from: &SocketAddr) -> Result, Error> { + fn on_neighbours(&mut self, rlp: &Rlp, _node: &NodeId, from: &SocketAddr) -> Result, Error> { // TODO: validate packet let mut added = HashMap::new(); trace!(target: "discovery", "Got {} Neighbours from {:?}", rlp.at(0)?.item_count()?, &from); @@ -510,7 +494,7 @@ impl Discovery { for bucket in &mut self.node_buckets { bucket.nodes.retain(|node| { if let Some(timeout) = node.timeout { - if !force && now.duration_since(timeout) < Duration::from_millis(PING_TIMEOUT_MS) { + if !force && now.duration_since(timeout) < PING_TIMEOUT { true } else { @@ -536,19 +520,16 @@ impl Discovery { self.start(); } - pub fn register_socket(&self, event_loop: &mut EventLoop) -> Result<(), Error> { - event_loop.register(&self.udp_socket, Token(self.token), Ready::all(), PollOpt::edge()).expect("Error registering UDP socket"); - Ok(()) + pub fn any_sends_queued(&self) -> bool { + !self.send_queue.is_empty() } - pub fn update_registration(&self, event_loop: &mut EventLoop) -> Result<(), Error> { - let registration = if !self.send_queue.is_empty() { - Ready::readable() | Ready::writable() - } else { - Ready::readable() - }; - event_loop.reregister(&self.udp_socket, Token(self.token), registration, PollOpt::edge()).expect("Error reregistering UDP socket"); - Ok(()) + pub fn dequeue_send(&mut self) -> Option { + self.send_queue.pop_front() + } + + pub fn requeue_send(&mut self, datagram: Datagram) { + self.send_queue.push_front(datagram) } } @@ -585,8 +566,8 @@ mod tests { let key2 = Random.generate().unwrap(); let ep1 = NodeEndpoint { address: SocketAddr::from_str("127.0.0.1:40444").unwrap(), udp_port: 40444 }; let ep2 = NodeEndpoint { address: SocketAddr::from_str("127.0.0.1:40445").unwrap(), udp_port: 40445 }; - let mut discovery1 = Discovery::new(&key1, ep1.address.clone(), ep1.clone(), 0, IpFilter::default()); - let mut discovery2 = Discovery::new(&key2, ep2.address.clone(), ep2.clone(), 0, IpFilter::default()); + let mut discovery1 = Discovery::new(&key1, ep1.clone(), IpFilter::default()); + let mut discovery2 = Discovery::new(&key2, ep2.clone(), IpFilter::default()); let node1 = Node::from_str("enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@127.0.0.1:7770").unwrap(); let node2 = Node::from_str("enode://b979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@127.0.0.1:7771").unwrap(); @@ -597,32 +578,30 @@ mod tests { discovery2.refresh(); for _ in 0 .. 10 { - while !discovery1.send_queue.is_empty() { - let datagramm = discovery1.send_queue.pop_front().unwrap(); - if datagramm.address == ep2.address { - discovery2.on_packet(&datagramm.payload, ep1.address.clone()).ok(); + while let Some(datagram) = discovery1.dequeue_send() { + if datagram.address == ep2.address { + discovery2.on_packet(&datagram.payload, ep1.address.clone()).ok(); } } - while !discovery2.send_queue.is_empty() { - let datagramm = discovery2.send_queue.pop_front().unwrap(); - if datagramm.address == ep1.address { - discovery1.on_packet(&datagramm.payload, ep2.address.clone()).ok(); + while let Some(datagram) = discovery2.dequeue_send() { + if datagram.address == ep1.address { + discovery1.on_packet(&datagram.payload, ep2.address.clone()).ok(); } } discovery2.round(); } - assert_eq!(Discovery::nearest_node_entries(&NodeId::new(), &discovery2.node_buckets).len(), 3) + assert_eq!(discovery2.nearest_node_entries(&NodeId::new()).len(), 3) } #[test] fn removes_expired() { let key = Random.generate().unwrap(); let ep = NodeEndpoint { address: SocketAddr::from_str("127.0.0.1:40446").unwrap(), udp_port: 40447 }; - let mut discovery = Discovery::new(&key, ep.address.clone(), ep.clone(), 0, IpFilter::default()); + let mut discovery = Discovery::new(&key, ep.clone(), IpFilter::default()); for _ in 0..1200 { discovery.add_node(NodeEntry { id: NodeId::random(), endpoint: ep.clone() }); } - assert!(Discovery::nearest_node_entries(&NodeId::new(), &discovery.node_buckets).len() <= 16); + assert!(discovery.nearest_node_entries(&NodeId::new()).len() <= 16); let removed = discovery.check_expired(true).len(); assert!(removed > 0); } @@ -630,24 +609,115 @@ mod tests { #[test] fn find_nearest_saturated() { use super::*; - let mut buckets: Vec<_> = (0..256).map(|_| NodeBucket::new()).collect(); + + let key = Random.generate().unwrap(); let ep = NodeEndpoint { address: SocketAddr::from_str("127.0.0.1:40447").unwrap(), udp_port: 40447 }; + let mut discovery = Discovery::new(&key, ep.clone(), IpFilter::default()); + for _ in 0..(16 + 10) { - buckets[0].nodes.push_back(BucketEntry { + discovery.node_buckets[0].nodes.push_back(BucketEntry { address: NodeEntry { id: NodeId::new(), endpoint: ep.clone() }, timeout: None, id_hash: keccak(NodeId::new()), }); } - let nearest = Discovery::nearest_node_entries(&NodeId::new(), &buckets); + let nearest = discovery.nearest_node_entries(&NodeId::new()); assert_eq!(nearest.len(), 16) } + #[test] + fn routing_table_insertions_lookups() { + use super::*; + let ep = NodeEndpoint { address: SocketAddr::from_str("127.0.0.1:40448").unwrap(), udp_port: 40447 }; + let node_ids_hex: [&str; 32] = [ + "22536fa57acc12c4993295cbc26fef4550513496712b301ad2283d356c8108521244a362e64e6d907a0d0b4e65526699c5ae3cfebfc680505fe3b33d50672835", + "22c482f42401546f8dd7ed6b1c0cad976da6630730f1116614579ccb084791a528ff2676bfe94434de80e5d7e479f1ea1d7737077da3bd5e69a0f3e5bf596091", + "234c73e3a8f6835a7f9a9d2a896bff4908d66d21d5433a2c37d94f1fa9a6ca17d02388f31013ff87e3ad86506e76bd1006b9cac3815974a2b47c8d4f2124697e", + "2a5aaf4e2046c521e890dc82313c6151a55078f045a7e3d259f168238d029271cdd9a0943468d45c1e36a34a8a6d4de4b0262e48d3c8cfdd4c2aab5df42926b9", + "341d8c94d9670461186cfc1f66d4246cb12384940e9f621ec8d6c216b5d037cde5f7a41b70474ca36ced4a4f2fe91c9dc5a24a128414672661f78e8611d54bfd", + "3d9fd01851f3ae1bfd06b48e89738f29f9a2b4dce3ab7864df4fccca55d1ac88044956ba47d0c4cb44a19924626a3a3aa5a4de8958365cb7385111ce7b929200", + "406d5507a7fbc194a495800ae8cf408093336febc24d03d6c63756f522274ab02146ceb1b0213291a9a1544680503837519f88f1e8677d921de62c82935b4e6c", + "4c537f00805f320616ee49c7bc36e1d7e52a04a782b0cc00fd3d6b77200b027cef5f875ed38f1167fef4b02d7bd49a661812301d9d680bb62297131204c035f9", + "4fc8e3fdbdd7acad82b283ac52c121b805f3b15ffcaa6b2ca67b9e375aa88e978951ffa3d03ee13be99f0ee987db0bbfc6a7ca02b175e9123d79826025b4089d", + "55b5042a6910bc908a0520966e8cbcc92ac299bdb7efbfbcf703df1506fa0f9b09c5eeb930080de848d2864cca71f885942852c51233db0ee46fe0447306d61f", + "5d24f28b350c4c37fc4dad7f418e029992c9e4ac356bb3d9a1356ba1076339863c05044d7ceba233c65779401f8a3b38fe67b6a592c1be4834dc869f7bb932eb", + "5f6edaf2f2ae3003f4b4ff90b8e71a717c832c71a634d96e77fe046f9a88adc8de5718ff3c47659aea4cead5376df5b731e1b6530e6b0999f56ad75d4dabd3f6", + "6214c04211efe91abd23d65e2dc8e711b06d4fb13dcfd65b691dc51f58455b2145f9b38f523b72a45a12705a28d389308a34455720d774c9b805326df42b5a63", + "69df92573ddbbce88b72a930843dbb70728b2a020e0cc4e8ba805dcf7f19297bfc5def4ca447e9e6ec66971be1815b8f49042720431f698b6a87a185d94fa6c8", + "72ffc23de007cf8b6f4a117f7427b532d05861c314344ffa265175f57ee45dae041a710a4dc74124dba1dabdc0f52dfd21e3154d1d4285aab529810c6161d623", + "80b567f279a9512f3a66ebd8f87a93acd4d50bf66f5eff6d04039c1f5838e37021e981539659b33e0644b243fc9671209a80cbef40d1bcf7c7117d353cb45532", + "9009dc9e3bf50595f84271f46d4c7a5ad6971f7d2ffce1905bfc40a407d34fc5e2dcebd92746eadcd2c5fa4d5aaccb0e01b542d506b361851df3f19e6bc629a3", + "95264f56e091efeba911003fd01eeb2c81f6fc4bb7b10c92e4c7bfaf460b7246d232e61ad8a223d74870981a84e15b2d5134c25d931cb860c6912b20a2d3ac01", + "96013a472a9f7ff9c5c76b5ca958f14ee510d826703aa41d4c88eac51d30d14229b9f19f6e0469c37aaa6d2136a978a4aaa38ca766f48e53e569f84e44252962", + "a513c988cf8480ad2992caa64e3fa059ce07efda260dfeefed78e1d41ea3f97844603b8a9737eb633086fd9ac2f201200cb656cda8a91bf6cc500d6039db6f53", + "ab3311f38e3641c8b3b1fd36dd7f94b148166e267258e840d29d1859537c74f202bd3342359b3623f96c23fa662d1b65182a898bf20343744b37cb265182e500", + "ac8f41dbd637891a08c9cf715c23577bdd431ba40231682a5a9ba7fd6cb6d66c04f63d6d65c7d9f8737e641e05fdbeede57138a174f0d55e7835575dd6cddd98", + "accdad251888d53e4e18efee1e0d749d050216b14896efb657e9c7b1b78dab82a5b6fb3234017aa19a2f50475d73960f352d308b2e0e841cbebaf418362a4f21", + "b138622208f74d2b8e8fc10bcd4cf3302685cd77d339280a939474b92be8b93e441c50709e25c82cc88a2a4207e9f2938912d60600226efe322b43c6ef5e7aef", + "b4f64e1fa6a5cd6198b2515bde63fbdabaf7e7a31dbaf5369babbda4b8cd0bf5025ac4b7d2d6e6e3bc76c890df585d28d4815e464c8792ef677df9206864a12b", + "c1136e08a27c93812ae2dd47201d9e81c82d1995001b88dba9eec700e1d3385dfaf7ae834226c3c90a138f1808cd10b5502f49ee774a2bc707f34bd7d160b7bd", + "c203ae9b5d1953b0ac462e66338800ec26982e2af54bd444fc8978973191633d4f483e31b28233c07bb99f34d57c680fa5f8e093e64f13b235005b7ab6e2d594", + "c2e1067c58a9948e773e0a3637d946e26d95762f89ec9d35e2ad84f770309d94168d4e112c78d62b60efc6216bc5d31475f24307b1b8e0fa8dcbb18a10cb85f5", + "d60ecb1a89e0d5aeff14c9a95da9f5492eb15871c53563b86b7c5ddf0da74b4c29e682fdd22aae2290e0b16ef4b6d707ef55396ca98f755c95b689cf65ce5f80", + "df5ad4ea6242929df86f2162d1cc62b0e0a6f0a03428a39dea98f6a689335b5ceaf1f0696c17b717b141aeb45a29108d95c3a7d2d1d0bb3441219504ae672917", + "e1268f5dd9552a11989df9d4953bb388e7466711b2bd9882a3ed4d0767a21f046c53c20f9a18d66bae1d6a5544492857ddecb0b5b4818bd4557be252ddd66c71", + "e626019dc0b50b9e254461f19d29e69a4669c5256134a6352c6c30d3bc55d201a5b43fc2e006556cfaf29765b683e807e03093798942826244e4ee9e47c75d3f", + ]; + let node_entries = node_ids_hex.iter() + .map(|node_id_hex| NodeId::from_str(node_id_hex).unwrap()) + .map(|node_id| NodeEntry { id: node_id, endpoint: ep.clone() }) + .collect::>(); + + let secret_hex = "6c71d1b8930d29e6371be1081f2c909c64b46440a1716314c3c9df995cb3aed1"; + let key = Secret::from_str(secret_hex) + .and_then(|secret| KeyPair::from_secret(secret)) + .unwrap(); + let mut discovery = Discovery::new(&key, ep.clone(), IpFilter::default()); + + node_entries.iter().for_each(|entry| discovery.update_node(entry.clone())); + + let expected_bucket_sizes = vec![ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 7, 8, 12 + ]; + let actual_bucket_sizes = discovery.node_buckets.iter() + .map(|ref bucket| bucket.nodes.len()) + .collect::>(); + assert_eq!(actual_bucket_sizes, expected_bucket_sizes); + + for entry in &node_entries { + let nearest = discovery.nearest_node_entries(&entry.id); + assert_eq!(nearest.len(), 16); + assert_eq!(nearest[0].id, entry.id); + + let mut expected_ids: Vec = node_entries.iter().map(|entry| entry.id).collect(); + expected_ids.sort_unstable_by_key(|id| keccak(id) ^ keccak(entry.id)); + expected_ids.resize(BUCKET_SIZE, NodeId::default()); + + let actual_ids: Vec = nearest.iter().map(|entry| entry.id).collect(); + assert_eq!(actual_ids, expected_ids); + } + } + #[test] fn packets() { let key = Random.generate().unwrap(); - let ep = NodeEndpoint { address: SocketAddr::from_str("127.0.0.1:40447").unwrap(), udp_port: 40447 }; - let mut discovery = Discovery::new(&key, ep.address.clone(), ep.clone(), 0, IpFilter::default()); + let ep = NodeEndpoint { address: SocketAddr::from_str("127.0.0.1:40449").unwrap(), udp_port: 40449 }; + let mut discovery = Discovery::new(&key, ep.clone(), IpFilter::default()); discovery.check_timestamps = false; let from = SocketAddr::from_str("99.99.99.99:40445").unwrap(); @@ -714,15 +784,15 @@ mod tests { let key2 = Random.generate().unwrap(); let ep1 = NodeEndpoint { address: SocketAddr::from_str("127.0.0.1:40344").unwrap(), udp_port: 40344 }; let ep2 = NodeEndpoint { address: SocketAddr::from_str("127.0.0.1:40345").unwrap(), udp_port: 40345 }; - let mut discovery1 = Discovery::new(&key1, ep1.address.clone(), ep1.clone(), 0, IpFilter::default()); - let mut discovery2 = Discovery::new(&key2, ep2.address.clone(), ep2.clone(), 0, IpFilter::default()); + let mut discovery1 = Discovery::new(&key1, ep1.clone(), IpFilter::default()); + let mut discovery2 = Discovery::new(&key2, ep2.clone(), IpFilter::default()); discovery1.ping(&ep2); - let ping_data = discovery1.send_queue.pop_front().unwrap(); + let ping_data = discovery1.dequeue_send().unwrap(); discovery2.on_packet(&ping_data.payload, ep1.address.clone()).ok(); - let pong_data = discovery2.send_queue.pop_front().unwrap(); + let pong_data = discovery2.dequeue_send().unwrap(); let data = &pong_data.payload[(32 + 65)..]; - let rlp = UntrustedRlp::new(&data[1..]); + let rlp = Rlp::new(&data[1..]); assert_eq!(ping_data.payload[0..32], rlp.val_at::>(1).unwrap()[..]) } } diff --git a/util/network-devp2p/src/handshake.rs b/util/network-devp2p/src/handshake.rs index 240046a98e12007bca70b83d63b455cb2df9ea3a..18869de55fcd2c297f41a329e884137ff709f1b9 100644 --- a/util/network-devp2p/src/handshake.rs +++ b/util/network-devp2p/src/handshake.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -14,20 +14,20 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use std::sync::Arc; +use std::time::Duration; use rand::random; use hash::write_keccak; use mio::tcp::*; use ethereum_types::{H256, H520}; use ethcore_bytes::Bytes; -use rlp::*; +use rlp::{Rlp, RlpStream}; use connection::{Connection}; use node_table::NodeId; -use stats::NetworkStats; use io::{IoContext, StreamToken}; use ethkey::{KeyPair, Public, Secret, recover, sign, Generator, Random}; -use crypto::{ecdh, ecies}; -use network::{Error, ErrorKind, HostInfo}; +use ethkey::crypto::{ecdh, ecies}; +use network::{Error, ErrorKind}; +use host::HostInfo; #[derive(PartialEq, Eq, Debug)] enum HandshakeState { @@ -75,21 +75,21 @@ pub struct Handshake { const V4_AUTH_PACKET_SIZE: usize = 307; const V4_ACK_PACKET_SIZE: usize = 210; -const HANDSHAKE_TIMEOUT: u64 = 5000; +const HANDSHAKE_TIMEOUT: Duration = Duration::from_secs(5); const PROTOCOL_VERSION: u64 = 4; // Amount of bytes added when encrypting with encryptECIES. const ECIES_OVERHEAD: usize = 113; impl Handshake { /// Create a new handshake object - pub fn new(token: StreamToken, id: Option<&NodeId>, socket: TcpStream, nonce: &H256, stats: Arc) -> Result { + pub fn new(token: StreamToken, id: Option<&NodeId>, socket: TcpStream, nonce: &H256) -> Result { Ok(Handshake { - id: if let Some(id) = id { id.clone()} else { NodeId::new() }, - connection: Connection::new(token, socket, stats), + id: if let Some(id) = id { *id } else { NodeId::new() }, + connection: Connection::new(token, socket), originated: false, state: HandshakeState::New, ecdhe: Random.generate()?, - nonce: nonce.clone(), + nonce: *nonce, remote_ephemeral: Public::new(), remote_nonce: H256::new(), remote_version: PROTOCOL_VERSION, @@ -166,7 +166,7 @@ impl Handshake { self.remote_version = remote_version; let shared = *ecdh::agree(host_secret, &self.id)?; let signature = H520::from_slice(sig); - self.remote_ephemeral = recover(&signature.into(), &(&shared ^ &self.remote_nonce))?; + self.remote_ephemeral = recover(&signature.into(), &(shared ^ self.remote_nonce))?; Ok(()) } @@ -189,7 +189,7 @@ impl Handshake { } Err(_) => { // Try to interpret as EIP-8 packet - let total = (((data[0] as u16) << 8 | (data[1] as u16)) as usize) + 2; + let total = ((u16::from(data[0]) << 8 | (u16::from(data[1]))) as usize) + 2; if total < V4_AUTH_PACKET_SIZE { debug!(target: "network", "Wrong EIP8 auth packet size"); return Err(ErrorKind::BadProtocol.into()); @@ -206,7 +206,7 @@ impl Handshake { trace!(target: "network", "Received EIP8 handshake auth from {:?}", self.connection.remote_addr_str()); self.auth_cipher.extend_from_slice(data); let auth = ecies::decrypt(secret, &self.auth_cipher[0..2], &self.auth_cipher[2..])?; - let rlp = UntrustedRlp::new(&auth); + let rlp = Rlp::new(&auth); let signature: H520 = rlp.val_at(0)?; let remote_public: Public = rlp.val_at(1)?; let remote_nonce: H256 = rlp.val_at(2)?; @@ -232,7 +232,7 @@ impl Handshake { } Err(_) => { // Try to interpret as EIP-8 packet - let total = (((data[0] as u16) << 8 | (data[1] as u16)) as usize) + 2; + let total = (((u16::from(data[0])) << 8 | (u16::from(data[1]))) as usize) + 2; if total < V4_ACK_PACKET_SIZE { debug!(target: "network", "Wrong EIP8 ack packet size"); return Err(ErrorKind::BadProtocol.into()); @@ -249,7 +249,7 @@ impl Handshake { trace!(target: "network", "Received EIP8 handshake auth from {:?}", self.connection.remote_addr_str()); self.ack_cipher.extend_from_slice(data); let ack = ecies::decrypt(secret, &self.ack_cipher[0..2], &self.ack_cipher[2..])?; - let rlp = UntrustedRlp::new(&ack); + let rlp = Rlp::new(&ack); self.remote_ephemeral = rlp.val_at(0)?; self.remote_nonce = rlp.val_at(1)?; self.remote_version = rlp.val_at(2)?; @@ -329,13 +329,11 @@ impl Handshake { #[cfg(test)] mod test { - use std::sync::Arc; use rustc_hex::FromHex; use super::*; use ethereum_types::H256; use io::*; use mio::tcp::TcpStream; - use stats::NetworkStats; use ethkey::Public; fn check_auth(h: &Handshake, version: u64) { @@ -355,7 +353,7 @@ mod test { let addr = "127.0.0.1:50556".parse().unwrap(); let socket = TcpStream::connect(&addr).unwrap(); let nonce = H256::new(); - Handshake::new(0, to, socket, &nonce, Arc::new(NetworkStats::new())).unwrap() + Handshake::new(0, to, socket, &nonce).unwrap() } fn test_io() -> IoContext { @@ -517,4 +515,3 @@ mod test { check_ack(&h, 57); } } - diff --git a/util/network-devp2p/src/host.rs b/util/network-devp2p/src/host.rs index c23527c328e060297967a138b1dd5547e6af7f7a..0fbd64b420945ea1e7e546daeacdec83a3abd9fc 100644 --- a/util/network-devp2p/src/host.rs +++ b/util/network-devp2p/src/host.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -24,27 +24,28 @@ use std::cmp::{min, max}; use std::path::{Path, PathBuf}; use std::io::{Read, Write, self}; use std::fs; +use std::time::Duration; use ethkey::{KeyPair, Secret, Random, Generator}; use hash::keccak; use mio::*; use mio::deprecated::{EventLoop}; use mio::tcp::*; +use mio::udp::*; use ethereum_types::H256; -use rlp::*; +use rlp::{RlpStream, Encodable}; + use session::{Session, SessionData}; use io::*; use PROTOCOL_VERSION; use node_table::*; use network::{NetworkConfiguration, NetworkIoMessage, ProtocolId, PeerId, PacketId}; use network::{NonReservedPeerMode, NetworkContext as NetworkContextTrait}; -use network::HostInfo as HostInfoTrait; use network::{SessionInfo, Error, ErrorKind, DisconnectReason, NetworkProtocolHandler}; -use stats::NetworkStats; -use discovery::{Discovery, TableUpdates, NodeEntry}; +use discovery::{Discovery, TableUpdates, NodeEntry, MAX_DATAGRAM_SIZE}; use ip_utils::{map_external_address, select_public_address}; use path::restrict_permissions_owner; use parking_lot::{Mutex, RwLock}; -use connection_filter::{ConnectionFilter, ConnectionDirection}; +use network::{ConnectionFilter, ConnectionDirection}; type Slab = ::slab::Slab; @@ -67,18 +68,20 @@ const SYS_TIMER: TimerToken = LAST_SESSION + 1; // Timeouts // for IDLE TimerToken -const MAINTENANCE_TIMEOUT: u64 = 1000; +const MAINTENANCE_TIMEOUT: Duration = Duration::from_secs(1); // for DISCOVERY_REFRESH TimerToken -const DISCOVERY_REFRESH_TIMEOUT: u64 = 60_000; +const DISCOVERY_REFRESH_TIMEOUT: Duration = Duration::from_secs(60); // for DISCOVERY_ROUND TimerToken -const DISCOVERY_ROUND_TIMEOUT: u64 = 300; +const DISCOVERY_ROUND_TIMEOUT: Duration = Duration::from_millis(300); // for NODE_TABLE TimerToken -const NODE_TABLE_TIMEOUT: u64 = 300_000; +const NODE_TABLE_TIMEOUT: Duration = Duration::from_secs(300); #[derive(Debug, PartialEq, Eq)] /// Protocol info pub struct CapabilityInfo { + /// Protocol ID pub protocol: ProtocolId, + /// Protocol version pub version: u8, /// Total number of packet IDs this protocol support. pub packet_count: u8, @@ -104,17 +107,20 @@ pub struct NetworkContext<'s> { impl<'s> NetworkContext<'s> { /// Create a new network IO access point. Takes references to all the data that can be updated within the IO handler. - fn new(io: &'s IoContext, + fn new( + io: &'s IoContext, protocol: ProtocolId, - session: Option, sessions: Arc>>, - reserved_peers: &'s HashSet) -> NetworkContext<'s> { + session: Option, + sessions: Arc>>, + reserved_peers: &'s HashSet, + ) -> NetworkContext<'s> { let id = session.as_ref().map(|s| s.lock().token()); NetworkContext { - io: io, - protocol: protocol, + io, + protocol, session_id: id, - session: session, - sessions: sessions, + session, + sessions, _reserved_peers: reserved_peers, } } @@ -147,10 +153,6 @@ impl<'s> NetworkContextTrait for NetworkContext<'s> { self.session_id.map_or_else(|| Err(ErrorKind::Expired.into()), |id| self.send(id, packet_id, data)) } - fn io_channel(&self) -> IoChannel { - self.io.channel() - } - fn disable_peer(&self, peer: PeerId) { self.io.message(NetworkIoMessage::DisablePeer(peer)) .unwrap_or_else(|e| warn!("Error sending network IO message: {:?}", e)); @@ -165,10 +167,10 @@ impl<'s> NetworkContextTrait for NetworkContext<'s> { self.session.as_ref().map_or(false, |s| s.lock().expired()) } - fn register_timer(&self, token: TimerToken, ms: u64) -> Result<(), Error> { + fn register_timer(&self, token: TimerToken, delay: Duration) -> Result<(), Error> { self.io.message(NetworkIoMessage::AddTimer { - token: token, - delay: ms, + token, + delay, protocol: self.protocol, }).unwrap_or_else(|e| warn!("Error sending network IO message: {:?}", e)); Ok(()) @@ -208,23 +210,23 @@ pub struct HostInfo { pub public_endpoint: Option, } -impl HostInfoTrait for HostInfo { - fn id(&self) -> &NodeId { - self.keys.public() - } - - fn secret(&self) -> &Secret { - self.keys.secret() - } - +impl HostInfo { fn next_nonce(&mut self) -> H256 { self.nonce = keccak(&self.nonce); self.nonce } - fn client_version(&self) -> &str { + pub(crate) fn client_version(&self) -> &str { &self.config.client_version } + + pub(crate) fn secret(&self) -> &Secret { + self.keys.secret() + } + + pub(crate) fn id(&self) -> &NodeId { + self.keys.public() + } } type SharedSession = Arc>; @@ -238,6 +240,7 @@ struct ProtocolTimer { /// Root IO handler. Manages protocol handlers, IO timers and network connections. pub struct Host { pub info: RwLock, + udp_socket: Mutex>, tcp_listener: Mutex, sessions: Arc>>, discovery: Mutex>, @@ -245,7 +248,6 @@ pub struct Host { handlers: RwLock>>, timers: RwLock>, timer_counter: RwLock, - stats: Arc, reserved_nodes: RwLock>, stopping: AtomicBool, filter: Option>, @@ -253,7 +255,7 @@ pub struct Host { impl Host { /// Create a new instance - pub fn new(mut config: NetworkConfiguration, stats: Arc, filter: Option>) -> Result { + pub fn new(mut config: NetworkConfiguration, filter: Option>) -> Result { let mut listen_address = match config.listen_address { None => SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), DEFAULT_PORT)), Some(addr) => addr, @@ -277,7 +279,7 @@ impl Host { let tcp_listener = TcpListener::bind(&listen_address)?; listen_address = SocketAddr::new(listen_address.ip(), tcp_listener.local_addr()?.port()); debug!(target: "network", "Listening at {:?}", listen_address); - let udp_port = config.udp_port.unwrap_or(listen_address.port()); + let udp_port = config.udp_port.unwrap_or_else(|| listen_address.port()); let local_endpoint = NodeEndpoint { address: listen_address, udp_port: udp_port }; let boot_nodes = config.boot_nodes.clone(); @@ -295,13 +297,13 @@ impl Host { local_endpoint: local_endpoint, }), discovery: Mutex::new(None), + udp_socket: Mutex::new(None), tcp_listener: Mutex::new(tcp_listener), sessions: Arc::new(RwLock::new(Slab::new_starting_at(FIRST_SESSION, MAX_SESSIONS))), nodes: RwLock::new(NodeTable::new(path)), handlers: RwLock::new(HashMap::new()), timers: RwLock::new(HashMap::new()), timer_counter: RwLock::new(USER_TIMER), - stats: stats, reserved_nodes: RwLock::new(HashSet::new()), stopping: AtomicBool::new(false), filter: filter, @@ -323,7 +325,7 @@ impl Host { match Node::from_str(id) { Err(e) => { debug!(target: "network", "Could not add node {}: {:?}", id, e); }, Ok(n) => { - let entry = NodeEntry { endpoint: n.endpoint.clone(), id: n.id.clone() }; + let entry = NodeEntry { endpoint: n.endpoint.clone(), id: n.id }; self.nodes.write().add_node(n); if let Some(ref mut discovery) = *self.discovery.lock() { @@ -336,9 +338,9 @@ impl Host { pub fn add_reserved_node(&self, id: &str) -> Result<(), Error> { let n = Node::from_str(id)?; - let entry = NodeEntry { endpoint: n.endpoint.clone(), id: n.id.clone() }; - self.reserved_nodes.write().insert(n.id.clone()); - self.nodes.write().add_node(Node::new(entry.id.clone(), entry.endpoint.clone())); + let entry = NodeEntry { endpoint: n.endpoint.clone(), id: n.id }; + self.reserved_nodes.write().insert(n.id); + self.nodes.write().add_node(Node::new(entry.id, entry.endpoint.clone())); if let Some(ref mut discovery) = *self.discovery.lock() { discovery.add_node(entry); @@ -347,10 +349,10 @@ impl Host { Ok(()) } - pub fn set_non_reserved_mode(&self, mode: NonReservedPeerMode, io: &IoContext) { + pub fn set_non_reserved_mode(&self, mode: &NonReservedPeerMode, io: &IoContext) { let mut info = self.info.write(); - if info.config.non_reserved_mode != mode { + if &info.config.non_reserved_mode != mode { info.config.non_reserved_mode = mode.clone(); drop(info); if let NonReservedPeerMode::Deny = mode { @@ -386,15 +388,15 @@ impl Host { pub fn external_url(&self) -> Option { let info = self.info.read(); - info.public_endpoint.as_ref().map(|e| format!("{}", Node::new(info.id().clone(), e.clone()))) + info.public_endpoint.as_ref().map(|e| format!("{}", Node::new(*info.id(), e.clone()))) } pub fn local_url(&self) -> String { let info = self.info.read(); - format!("{}", Node::new(info.id().clone(), info.local_endpoint.clone())) + format!("{}", Node::new(*info.id(), info.local_endpoint.clone())) } - pub fn stop(&self, io: &IoContext) -> Result<(), Error> { + pub fn stop(&self, io: &IoContext) { self.stopping.store(true, AtomicOrdering::Release); let mut to_kill = Vec::new(); for e in self.sessions.read().iter() { @@ -406,8 +408,7 @@ impl Host { trace!(target: "network", "Disconnecting on shutdown: {}", p); self.kill_connection(p, io, true); } - io.unregister_handler()?; - Ok(()) + io.unregister_handler(); } /// Get all connected peers. @@ -460,13 +461,16 @@ impl Host { let discovery = { let info = self.info.read(); if info.config.discovery_enabled && info.config.non_reserved_mode == NonReservedPeerMode::Accept { - let mut udp_addr = local_endpoint.address.clone(); - udp_addr.set_port(local_endpoint.udp_port); - Some(Discovery::new(&info.keys, udp_addr, public_endpoint, DISCOVERY, allow_ips)) + Some(Discovery::new(&info.keys, public_endpoint, allow_ips)) } else { None } }; if let Some(mut discovery) = discovery { + let mut udp_addr = local_endpoint.address; + udp_addr.set_port(local_endpoint.udp_port); + let socket = UdpSocket::bind(&udp_addr).expect("Error binding UDP socket"); + *self.udp_socket.lock() = Some(socket); + discovery.init_node_list(self.nodes.read().entries()); discovery.add_node_list(self.nodes.read().entries()); *self.discovery.lock() = Some(discovery); @@ -553,7 +557,7 @@ impl Host { // iterate over all nodes, reserved ones coming first. // if we are pinned to only reserved nodes, ignore all others. let nodes = reserved_nodes.iter().cloned().chain(if !pin { - self.nodes.read().nodes(allow_ips) + self.nodes.read().nodes(&allow_ips) } else { Vec::new() }); @@ -586,21 +590,20 @@ impl Host { let address = { let mut nodes = self.nodes.write(); if let Some(node) = nodes.get_mut(id) { - node.attempts += 1; node.endpoint.address - } - else { + } else { debug!(target: "network", "Connection to expired node aborted"); return; } }; match TcpStream::connect(&address) { Ok(socket) => { - trace!(target: "network", "Connecting to {:?}", address); + trace!(target: "network", "{}: Connecting to {:?}", id, address); socket }, Err(e) => { - debug!(target: "network", "Can't connect to address {:?}: {:?}", address, e); + debug!(target: "network", "{}: Can't connect to address {:?}: {:?}", id, address, e); + self.nodes.write().note_failure(&id); return; } } @@ -616,7 +619,8 @@ impl Host { let mut sessions = self.sessions.write(); let token = sessions.insert_with_opt(|token| { - match Session::new(io, socket, token, id, &nonce, self.stats.clone(), &self.info.read()) { + trace!(target: "network", "{}: Initiating session {:?}", token, id); + match Session::new(io, socket, token, id, &nonce, &self.info.read()) { Ok(s) => Some(Arc::new(Mutex::new(s))), Err(e) => { debug!(target: "network", "Session create error: {:?}", e); @@ -685,12 +689,17 @@ impl Host { Err(e) => { let s = session.lock(); trace!(target: "network", "Session read error: {}:{:?} ({:?}) {:?}", token, s.id(), s.remote_addr(), e); - if let ErrorKind::Disconnect(DisconnectReason::IncompatibleProtocol) = *e.kind() { - if let Some(id) = s.id() { - if !self.reserved_nodes.read().contains(id) { - self.nodes.write().mark_as_useless(id); + match *e.kind() { + ErrorKind::Disconnect(DisconnectReason::IncompatibleProtocol) | ErrorKind::Disconnect(DisconnectReason::UselessPeer) => { + if let Some(id) = s.id() { + if !self.reserved_nodes.read().contains(id) { + let mut nodes = self.nodes.write(); + nodes.note_failure(&id); + nodes.mark_as_useless(id); + } } - } + }, + _ => {}, } kill = true; break; @@ -746,7 +755,7 @@ impl Host { let entry = NodeEntry { id: id, endpoint: endpoint }; let mut nodes = self.nodes.write(); if !nodes.contains(&entry.id) { - nodes.add_node(Node::new(entry.id.clone(), entry.endpoint.clone())); + nodes.add_node(Node::new(entry.id, entry.endpoint.clone())); let mut discovery = self.discovery.lock(); if let Some(ref mut discovery) = *discovery { discovery.add_node(entry); @@ -754,6 +763,10 @@ impl Host { } } } + + // Note connection success + self.nodes.write().note_success(&id); + for (p, _) in self.handlers.read().iter() { if s.have_capability(*p) { ready_data.push(*p); @@ -793,7 +806,6 @@ impl Host { return; } for p in ready_data { - self.stats.inc_sessions(); let reserved = self.reserved_nodes.read(); if let Some(h) = handlers.get(&p).clone() { h.connected(&NetworkContext::new(io, p, Some(session.clone()), self.sessions.clone(), &reserved), &token); @@ -813,6 +825,67 @@ impl Host { } } + fn discovery_readable(&self, io: &IoContext) { + let node_changes = match (self.udp_socket.lock().as_ref(), self.discovery.lock().as_mut()) { + (Some(udp_socket), Some(discovery)) => { + let mut buf = [0u8; MAX_DATAGRAM_SIZE]; + let writable = discovery.any_sends_queued(); + let res = match udp_socket.recv_from(&mut buf) { + Ok(Some((len, address))) => discovery.on_packet(&buf[0..len], address).unwrap_or_else(|e| { + debug!(target: "network", "Error processing UDP packet: {:?}", e); + None + }), + Ok(_) => None, + Err(e) => { + debug!(target: "network", "Error reading UPD socket: {:?}", e); + None + } + }; + let new_writable = discovery.any_sends_queued(); + if writable != new_writable { + io.update_registration(DISCOVERY) + .unwrap_or_else(|e| { + debug!(target: "network" ,"Error updating discovery registration: {:?}", e) + }); + } + res + }, + _ => None, + }; + if let Some(node_changes) = node_changes { + self.update_nodes(io, node_changes); + } + } + + fn discovery_writable(&self, io: &IoContext) { + match (self.udp_socket.lock().as_ref(), self.discovery.lock().as_mut()) { + (Some(udp_socket), Some(discovery)) => { + while let Some(data) = discovery.dequeue_send() { + match udp_socket.send_to(&data.payload, &data.address) { + Ok(Some(size)) if size == data.payload.len() => { + }, + Ok(Some(_)) => { + warn!(target: "network", "UDP sent incomplete datagram"); + }, + Ok(None) => { + discovery.requeue_send(data); + return; + } + Err(e) => { + debug!(target: "network", "UDP send error: {:?}, address: {:?}", e, &data.address); + return; + } + } + } + io.update_registration(DISCOVERY) + .unwrap_or_else(|e| { + debug!(target: "network", "Error updating discovery registration: {:?}", e) + }); + }, + _ => (), + } + } + fn connection_timeout(&self, token: StreamToken, io: &IoContext) { trace!(target: "network", "Connection timeout: {}", token); self.kill_connection(token, io, true) @@ -914,12 +987,7 @@ impl IoHandler for Host { } match stream { FIRST_SESSION ... LAST_SESSION => self.session_readable(stream, io), - DISCOVERY => { - let node_changes = { self.discovery.lock().as_mut().map_or(None, |d| d.readable(io)) }; - if let Some(node_changes) = node_changes { - self.update_nodes(io, node_changes); - } - }, + DISCOVERY => self.discovery_readable(io), TCP_ACCEPT => self.accept(io), _ => panic!("Received unknown readable token"), } @@ -931,9 +999,7 @@ impl IoHandler for Host { } match stream { FIRST_SESSION ... LAST_SESSION => self.session_writable(stream, io), - DISCOVERY => { - self.discovery.lock().as_mut().map(|d| d.writable(io)); - } + DISCOVERY => self.discovery_writable(io), _ => panic!("Received unknown writable token"), } } @@ -983,18 +1049,20 @@ impl IoHandler for Host { ref handler, ref protocol, ref versions, - ref packet_count, } => { let h = handler.clone(); let reserved = self.reserved_nodes.read(); h.initialize( &NetworkContext::new(io, *protocol, None, self.sessions.clone(), &reserved), - &*self.info.read(), ); self.handlers.write().insert(*protocol, h); let mut info = self.info.write(); - for v in versions { - info.capabilities.push(CapabilityInfo { protocol: *protocol, version: *v, packet_count: *packet_count }); + for &(version, packet_count) in versions { + info.capabilities.push(CapabilityInfo { + protocol: *protocol, + version, + packet_count, + }); } }, NetworkIoMessage::AddTimer { @@ -1025,7 +1093,9 @@ impl IoHandler for Host { if let Some(session) = session { session.lock().disconnect(io, DisconnectReason::DisconnectRequested); if let Some(id) = session.lock().id() { - self.nodes.write().mark_as_useless(id) + let mut nodes = self.nodes.write(); + nodes.note_failure(&id); + nodes.mark_as_useless(id); } } trace!(target: "network", "Disabling peer {}", peer); @@ -1045,7 +1115,13 @@ impl IoHandler for Host { session.lock().register_socket(reg, event_loop).expect("Error registering socket"); } } - DISCOVERY => self.discovery.lock().as_ref().and_then(|d| d.register_socket(event_loop).ok()).expect("Error registering discovery socket"), + DISCOVERY => match self.udp_socket.lock().as_ref() { + Some(udp_socket) => { + event_loop.register(udp_socket, reg, Ready::all(), PollOpt::edge()) + .expect("Error registering UDP socket"); + }, + _ => panic!("Error registering discovery socket"), + } TCP_ACCEPT => event_loop.register(&*self.tcp_listener.lock(), Token(TCP_ACCEPT), Ready::all(), PollOpt::edge()).expect("Error registering stream"), _ => warn!("Unexpected stream registration") } @@ -1076,7 +1152,18 @@ impl IoHandler for Host { connection.lock().update_socket(reg, event_loop).expect("Error updating socket"); } } - DISCOVERY => self.discovery.lock().as_ref().and_then(|d| d.update_registration(event_loop).ok()).expect("Error reregistering discovery socket"), + DISCOVERY => match (self.udp_socket.lock().as_ref(), self.discovery.lock().as_ref()) { + (Some(udp_socket), Some(discovery)) => { + let registration = if discovery.any_sends_queued() { + Ready::readable() | Ready::writable() + } else { + Ready::readable() + }; + event_loop.reregister(udp_socket, reg, registration, PollOpt::edge()) + .expect("Error reregistering UDP socket"); + }, + _ => panic!("Error reregistering discovery socket"), + } TCP_ACCEPT => event_loop.reregister(&*self.tcp_listener.lock(), Token(TCP_ACCEPT), Ready::all(), PollOpt::edge()).expect("Error reregistering stream"), _ => warn!("Unexpected stream update") } @@ -1101,7 +1188,7 @@ fn save_key(path: &Path, key: &Secret) { if let Err(e) = restrict_permissions_owner(path, true, false) { warn!(target: "network", "Failed to modify permissions of the file ({})", e); } - if let Err(e) = file.write(&key.hex().into_bytes()) { + if let Err(e) = file.write(&key.hex().into_bytes()[2..]) { warn!("Error writing key file: {:?}", e); } } @@ -1144,12 +1231,11 @@ fn key_save_load() { assert_eq!(key, r.unwrap()); } - #[test] fn host_client_url() { let mut config = NetworkConfiguration::new_local(); let key = "6f7b0d801bc7b5ce7bbd930b84fd0369b3eb25d09be58d64ba811091046f3aa2".parse().unwrap(); config.use_secret = Some(key); - let host: Host = Host::new(config, Arc::new(NetworkStats::new()), None).unwrap(); + let host: Host = Host::new(config, None).unwrap(); assert!(host.local_url().starts_with("enode://101b3ef5a4ea7a1c7928e24c4c75fd053c235d7b80c22ae5c03d145d0ac7396e2a4ffff9adee3133a7b05044a5cee08115fd65145e5165d646bde371010d803c@")); } diff --git a/util/network-devp2p/src/ip_utils.rs b/util/network-devp2p/src/ip_utils.rs index 3767fbb15a7851f1e40eb96a04652c31800bb9ce..a68fc51f1092fab07e1e431fe51de42882007a82 100644 --- a/util/network-devp2p/src/ip_utils.rs +++ b/util/network-devp2p/src/ip_utils.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -109,7 +109,7 @@ impl SocketAddrExt for Ipv4Addr { fn is_within(&self, ipnet: &IpNetwork) -> bool { match ipnet { - &IpNetwork::V4(ipnet) => ipnet.contains(*self), + IpNetwork::V4(ipnet) => ipnet.contains(*self), _ => false } } @@ -167,7 +167,7 @@ impl SocketAddrExt for Ipv6Addr { fn is_within(&self, ipnet: &IpNetwork) -> bool { match ipnet { - &IpNetwork::V6(ipnet) => ipnet.contains(*self), + IpNetwork::V6(ipnet) => ipnet.contains(*self), _ => false } } @@ -210,30 +210,30 @@ impl SocketAddrExt for IpAddr { } } -#[cfg(not(windows))] +#[cfg(not(any(windows, target_os = "android")))] mod getinterfaces { - use std::{mem, io, ptr}; + use std::{mem, io}; use libc::{AF_INET, AF_INET6}; use libc::{getifaddrs, freeifaddrs, ifaddrs, sockaddr, sockaddr_in, sockaddr_in6}; use std::net::{Ipv4Addr, Ipv6Addr, IpAddr}; fn convert_sockaddr(sa: *mut sockaddr) -> Option { - if sa == ptr::null_mut() { return None; } + if sa.is_null() { return None; } - let (addr, _) = match unsafe { *sa }.sa_family as i32 { + let (addr, _) = match i32::from(unsafe { *sa }.sa_family) { AF_INET => { - let sa: *const sockaddr_in = unsafe { mem::transmute(sa) }; - let sa = & unsafe { *sa }; + let sa: *const sockaddr_in = sa as *const sockaddr_in; + let sa = unsafe { &*sa }; let (addr, port) = (sa.sin_addr.s_addr, sa.sin_port); (IpAddr::V4(Ipv4Addr::new( - (addr & 0x000000FF) as u8, - ((addr & 0x0000FF00) >> 8) as u8, - ((addr & 0x00FF0000) >> 16) as u8, - ((addr & 0xFF000000) >> 24) as u8)), + (addr & 0x0000_00FF) as u8, + ((addr & 0x0000_FF00) >> 8) as u8, + ((addr & 0x00FF_0000) >> 16) as u8, + ((addr & 0xFF00_0000) >> 24) as u8)), port) }, AF_INET6 => { - let sa: *const sockaddr_in6 = unsafe { mem::transmute(sa) }; + let sa: *const sockaddr_in6 = sa as *const sockaddr_in6; let sa = & unsafe { *sa }; let (addr, port) = (sa.sin6_addr.s6_addr, sa.sin6_port); let addr: [u16; 8] = unsafe { mem::transmute(addr) }; @@ -266,7 +266,7 @@ mod getinterfaces { let mut ret = Vec::new(); let mut cur: *mut ifaddrs = ifap; - while cur != ptr::null_mut() { + while !cur.is_null() { if let Some(ip_addr) = convert_ifaddrs(cur) { ret.push(ip_addr); } @@ -280,12 +280,12 @@ mod getinterfaces { } } -#[cfg(not(windows))] +#[cfg(not(any(windows, target_os = "android")))] fn get_if_addrs() -> io::Result> { getinterfaces::get_all() } -#[cfg(windows)] +#[cfg(any(windows, target_os = "android"))] fn get_if_addrs() -> io::Result> { Ok(Vec::new()) } @@ -297,16 +297,16 @@ pub fn select_public_address(port: u16) -> SocketAddr { //prefer IPV4 bindings for addr in &list { //TODO: use better criteria than just the first in the list match addr { - &IpAddr::V4(a) if !a.is_reserved() => { - return SocketAddr::V4(SocketAddrV4::new(a, port)); + IpAddr::V4(a) if !a.is_reserved() => { + return SocketAddr::V4(SocketAddrV4::new(*a, port)); }, _ => {}, } } for addr in &list { match addr { - &IpAddr::V6(a) if !a.is_reserved() => { - return SocketAddr::V6(SocketAddrV6::new(a, port, 0, 0)); + IpAddr::V6(a) if !a.is_reserved() => { + return SocketAddr::V6(SocketAddrV6::new(*a, port, 0, 0)); }, _ => {}, } @@ -319,7 +319,7 @@ pub fn select_public_address(port: u16) -> SocketAddr { pub fn map_external_address(local: &NodeEndpoint) -> Option { if let SocketAddr::V4(ref local_addr) = local.address { - match search_gateway_from_timeout(local_addr.ip().clone(), Duration::new(5, 0)) { + match search_gateway_from_timeout(*local_addr.ip(), Duration::new(5, 0)) { Err(ref err) => debug!("Gateway search error: {}", err), Ok(gateway) => { match gateway.get_external_ip() { @@ -327,17 +327,17 @@ pub fn map_external_address(local: &NodeEndpoint) -> Option { debug!("IP request error: {}", err); }, Ok(external_addr) => { - match gateway.add_any_port(PortMappingProtocol::TCP, SocketAddrV4::new(local_addr.ip().clone(), local_addr.port()), 0, "Parity Node/TCP") { + match gateway.add_any_port(PortMappingProtocol::TCP, SocketAddrV4::new(*local_addr.ip(), local_addr.port()), 0, "Parity Node/TCP") { Err(ref err) => { debug!("Port mapping error: {}", err); }, Ok(tcp_port) => { - match gateway.add_any_port(PortMappingProtocol::UDP, SocketAddrV4::new(local_addr.ip().clone(), local.udp_port), 0, "Parity Node/UDP") { + match gateway.add_any_port(PortMappingProtocol::UDP, SocketAddrV4::new(*local_addr.ip(), local.udp_port), 0, "Parity Node/UDP") { Err(ref err) => { debug!("Port mapping error: {}", err); }, Ok(udp_port) => { - return Some(NodeEndpoint { address: SocketAddr::V4(SocketAddrV4::new(external_addr, tcp_port)), udp_port: udp_port }); + return Some(NodeEndpoint { address: SocketAddr::V4(SocketAddrV4::new(external_addr, tcp_port)), udp_port }); }, } }, @@ -533,5 +533,3 @@ fn ipv6_properties() { check("::", true, false, true); check("::1", false, true, false); } - - diff --git a/util/network-devp2p/src/lib.rs b/util/network-devp2p/src/lib.rs index 0dfda0b16e470172867fd366333cf531cd9a705b..01fc1fe25f45deec38540f33552a110c704f473d 100644 --- a/util/network-devp2p/src/lib.rs +++ b/util/network-devp2p/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -24,12 +24,13 @@ //! use net::*; //! use devp2p::NetworkService; //! use std::sync::Arc; +//! use std::time::Duration; //! //! struct MyHandler; //! //! impl NetworkProtocolHandler for MyHandler { -//! fn initialize(&self, io: &NetworkContext, _host_info: &HostInfo) { -//! io.register_timer(0, 1000); +//! fn initialize(&self, io: &NetworkContext) { +//! io.register_timer(0, Duration::from_secs(1)); //! } //! //! fn read(&self, io: &NetworkContext, peer: &PeerId, packet_id: u8, data: &[u8]) { @@ -48,7 +49,7 @@ //! fn main () { //! let mut service = NetworkService::new(NetworkConfiguration::new_local(), None).expect("Error creating network service"); //! service.start().expect("Error starting service"); -//! service.register_protocol(Arc::new(MyHandler), *b"myp", 1, &[1u8]); +//! service.register_protocol(Arc::new(MyHandler), *b"myp", &[(1u8, 1u8)]); //! //! // Wait for quit condition //! // ... @@ -61,6 +62,7 @@ extern crate ethcore_io as io; extern crate ethcore_bytes; +extern crate ethcore_crypto as crypto; extern crate ethereum_types; extern crate parking_lot; extern crate mio; @@ -73,7 +75,6 @@ extern crate igd; extern crate libc; extern crate slab; extern crate ethkey; -extern crate ethcrypto as crypto; extern crate rlp; extern crate bytes; extern crate path; @@ -94,6 +95,8 @@ extern crate serde_derive; #[cfg(test)] extern crate tempdir; +#[cfg(test)] #[macro_use] +extern crate assert_matches; mod host; mod connection; @@ -102,13 +105,9 @@ mod session; mod discovery; mod service; mod node_table; -mod stats; mod ip_utils; -mod connection_filter; pub use service::NetworkService; -pub use stats::NetworkStats; -pub use connection_filter::{ConnectionFilter, ConnectionDirection}; pub use host::NetworkContext; pub use io::TimerToken; diff --git a/util/network-devp2p/src/node_table.rs b/util/network-devp2p/src/node_table.rs index 244f518f218d34f41404c1603d810c8f7838466b..087caefe18e302df62c3c87a6ec1596ed1eb0a2a 100644 --- a/util/network-devp2p/src/node_table.rs +++ b/util/network-devp2p/src/node_table.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -14,19 +14,21 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +use discovery::{TableUpdates, NodeEntry}; +use ethereum_types::H512; +use ip_utils::*; +use network::{Error, ErrorKind, AllowIP, IpFilter}; +use rlp::{Rlp, RlpStream, DecoderError}; +use serde_json; use std::collections::{HashMap, HashSet}; use std::fmt::{self, Display, Formatter}; use std::hash::{Hash, Hasher}; use std::net::{SocketAddr, ToSocketAddrs, SocketAddrV4, SocketAddrV6, Ipv4Addr, Ipv6Addr}; use std::path::PathBuf; use std::str::FromStr; -use std::{fs, mem, slice}; -use ethereum_types::H512; -use rlp::*; -use network::{Error, ErrorKind, AllowIP, IpFilter}; -use discovery::{TableUpdates, NodeEntry}; -use ip_utils::*; -use serde_json; +use std::{fs, slice}; +use std::time::{self, Duration, SystemTime}; +use rand::{self, Rng}; /// Node public key pub type NodeId = H512; @@ -43,8 +45,8 @@ pub struct NodeEndpoint { impl NodeEndpoint { pub fn udp_address(&self) -> SocketAddr { match self.address { - SocketAddr::V4(a) => SocketAddr::V4(SocketAddrV4::new(a.ip().clone(), self.udp_port)), - SocketAddr::V6(a) => SocketAddr::V6(SocketAddrV6::new(a.ip().clone(), self.udp_port, a.flowinfo(), a.scope_id())), + SocketAddr::V4(a) => SocketAddr::V4(SocketAddrV4::new(*a.ip(), self.udp_port)), + SocketAddr::V6(a) => SocketAddr::V6(SocketAddrV6::new(*a.ip(), self.udp_port, a.flowinfo(), a.scope_id())), } } @@ -59,27 +61,27 @@ impl NodeEndpoint { pub fn is_allowed_by_predefined(&self, filter: &AllowIP) -> bool { match filter { - &AllowIP::All => true, - &AllowIP::Private => self.address.ip().is_usable_private(), - &AllowIP::Public => self.address.ip().is_usable_public(), - &AllowIP::None => false, + AllowIP::All => true, + AllowIP::Private => self.address.ip().is_usable_private(), + AllowIP::Public => self.address.ip().is_usable_public(), + AllowIP::None => false, } } - pub fn from_rlp(rlp: &UntrustedRlp) -> Result { + pub fn from_rlp(rlp: &Rlp) -> Result { let tcp_port = rlp.val_at::(2)?; let udp_port = rlp.val_at::(1)?; let addr_bytes = rlp.at(0)?.data()?; let address = match addr_bytes.len() { 4 => Ok(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(addr_bytes[0], addr_bytes[1], addr_bytes[2], addr_bytes[3]), tcp_port))), 16 => unsafe { - let o: *const u16 = mem::transmute(addr_bytes.as_ptr()); + let o: *const u16 = addr_bytes.as_ptr() as *const u16; let o = slice::from_raw_parts(o, 8); Ok(SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::new(o[0], o[1], o[2], o[3], o[4], o[5], o[6], o[7]), tcp_port, 0, 0))) }, _ => Err(DecoderError::RlpInconsistentLengthAndData) }?; - Ok(NodeEndpoint { address: address, udp_port: udp_port }) + Ok(NodeEndpoint { address, udp_port }) } pub fn to_rlp(&self, rlp: &mut RlpStream) { @@ -88,7 +90,7 @@ impl NodeEndpoint { rlp.append(&(&a.ip().octets()[..])); } SocketAddr::V6(a) => unsafe { - let o: *const u8 = mem::transmute(a.ip().segments().as_ptr()); + let o: *const u8 = a.ip().segments().as_ptr() as *const u8; rlp.append(&slice::from_raw_parts(o, 16)); } }; @@ -122,46 +124,70 @@ impl FromStr for NodeEndpoint { address: a, udp_port: a.port() }), - Ok(_) => Err(ErrorKind::AddressResolve(None).into()), - Err(e) => Err(ErrorKind::AddressResolve(Some(e)).into()) + Ok(None) => bail!(ErrorKind::AddressResolve(None)), + Err(_) => Err(ErrorKind::AddressParse.into()) // always an io::Error of InvalidInput kind } } } -#[derive(PartialEq, Eq, Copy, Clone)] +#[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum PeerType { _Required, Optional } +/// A type for representing an interaction (contact) with a node at a given time +/// that was either a success or a failure. +#[derive(Clone, Copy, Debug)] +pub enum NodeContact { + Success(SystemTime), + Failure(SystemTime), +} + +impl NodeContact { + fn success() -> NodeContact { + NodeContact::Success(SystemTime::now()) + } + + fn failure() -> NodeContact { + NodeContact::Failure(SystemTime::now()) + } + + fn time(&self) -> SystemTime { + match *self { + NodeContact::Success(t) | NodeContact::Failure(t) => t + } + } + + /// Filters and old contact, returning `None` if it happened longer than a + /// week ago. + fn recent(&self) -> Option<&NodeContact> { + let t = self.time(); + if let Ok(d) = t.elapsed() { + if d < Duration::from_secs(60 * 60 * 24 * 7) { + return Some(self); + } + } + + None + } +} + +#[derive(Debug)] pub struct Node { pub id: NodeId, pub endpoint: NodeEndpoint, pub peer_type: PeerType, - pub attempts: u32, - pub failures: u32, + pub last_contact: Option, } -const DEFAULT_FAILURE_PERCENTAGE: usize = 50; - impl Node { pub fn new(id: NodeId, endpoint: NodeEndpoint) -> Node { Node { - id: id, - endpoint: endpoint, + id, + endpoint, peer_type: PeerType::Optional, - attempts: 0, - failures: 0, - } - } - - /// Returns the node's failure percentage (0..100) in buckets of 5%. If there are 0 connection attempts for this - /// node the default failure percentage is returned (50%). - pub fn failure_percentage(&self) -> usize { - if self.attempts == 0 { - DEFAULT_FAILURE_PERCENTAGE - } else { - (self.failures * 100 / self.attempts / 5 * 5) as usize + last_contact: None, } } } @@ -169,9 +195,9 @@ impl Node { impl Display for Node { fn fmt(&self, f: &mut Formatter) -> fmt::Result { if self.endpoint.udp_port != self.endpoint.address.port() { - write!(f, "enode://{}@{}+{}", self.id.hex(), self.endpoint.address, self.endpoint.udp_port)?; + write!(f, "enode://{:x}@{}+{}", self.id, self.endpoint.address, self.endpoint.udp_port)?; } else { - write!(f, "enode://{}@{}", self.id.hex(), self.endpoint.address)?; + write!(f, "enode://{:x}@{}", self.id, self.endpoint.address)?; } Ok(()) } @@ -188,11 +214,10 @@ impl FromStr for Node { }; Ok(Node { - id: id, - endpoint: endpoint, + id, + endpoint, peer_type: PeerType::Optional, - attempts: 0, - failures: 0, + last_contact: None, }) } } @@ -231,33 +256,66 @@ impl NodeTable { /// Add a node to table pub fn add_node(&mut self, mut node: Node) { - // preserve attempts and failure counter - let (attempts, failures) = - self.nodes.get(&node.id).map_or((0, 0), |n| (n.attempts, n.failures)); - - node.attempts = attempts; - node.failures = failures; - - self.nodes.insert(node.id.clone(), node); - } - + // preserve node last_contact + node.last_contact = self.nodes.get(&node.id).and_then(|n| n.last_contact); + self.nodes.insert(node.id, node); + } + + /// Returns a list of ordered nodes according to their most recent contact + /// and filtering useless nodes. The algorithm for creating the sorted nodes + /// is: + /// - Contacts that aren't recent (older than 1 week) are discarded + /// - (1) Nodes with a successful contact are ordered (most recent success first) + /// - (2) Nodes with unknown contact (older than 1 week or new nodes) are randomly shuffled + /// - (3) Nodes with a failed contact are ordered (oldest failure first) + /// - The final result is the concatenation of (1), (2) and (3) fn ordered_entries(&self) -> Vec<&Node> { - let mut refs: Vec<&Node> = self.nodes.values() - .filter(|n| !self.useless_nodes.contains(&n.id)) - .collect(); + let mut success = Vec::new(); + let mut failures = Vec::new(); + let mut unknown = Vec::new(); + + let nodes = self.nodes.values() + .filter(|n| !self.useless_nodes.contains(&n.id)); + + for node in nodes { + // discard contact points older that aren't recent + match node.last_contact.as_ref().and_then(|c| c.recent()) { + Some(&NodeContact::Success(_)) => { + success.push(node); + }, + Some(&NodeContact::Failure(_)) => { + failures.push(node); + }, + None => { + unknown.push(node); + }, + } + } - refs.sort_by(|a, b| { - a.failure_percentage().cmp(&b.failure_percentage()) - .then_with(|| a.failures.cmp(&b.failures)) - .then_with(|| b.attempts.cmp(&a.attempts)) // we use reverse ordering for number of attempts + success.sort_by(|a, b| { + let a = a.last_contact.expect("vector only contains values with defined last_contact; qed"); + let b = b.last_contact.expect("vector only contains values with defined last_contact; qed"); + // inverse ordering, most recent successes come first + b.time().cmp(&a.time()) }); - refs + failures.sort_by(|a, b| { + let a = a.last_contact.expect("vector only contains values with defined last_contact; qed"); + let b = b.last_contact.expect("vector only contains values with defined last_contact; qed"); + // normal ordering, most distant failures come first + a.time().cmp(&b.time()) + }); + + rand::thread_rng().shuffle(&mut unknown); + + success.append(&mut unknown); + success.append(&mut failures); + success } /// Returns node ids sorted by failure percentage, for nodes with the same failure percentage the absolute number of /// failures is considered. - pub fn nodes(&self, filter: IpFilter) -> Vec { + pub fn nodes(&self, filter: &IpFilter) -> Vec { self.ordered_entries().iter() .filter(|n| n.endpoint.is_allowed(&filter)) .map(|n| n.id) @@ -269,7 +327,7 @@ impl NodeTable { pub fn entries(&self) -> Vec { self.ordered_entries().iter().map(|n| NodeEntry { endpoint: n.endpoint.clone(), - id: n.id.clone(), + id: n.id, }).collect() } @@ -286,7 +344,7 @@ impl NodeTable { /// Apply table changes coming from discovery pub fn update(&mut self, mut update: TableUpdates, reserved: &HashSet) { for (_, node) in update.added.drain() { - let entry = self.nodes.entry(node.id.clone()).or_insert_with(|| Node::new(node.id.clone(), node.endpoint.clone())); + let entry = self.nodes.entry(node.id).or_insert_with(|| Node::new(node.id, node.endpoint.clone())); entry.endpoint = node.endpoint; } for r in update.removed { @@ -296,10 +354,17 @@ impl NodeTable { } } - /// Increase failure counte for a node + /// Set last contact as failure for a node pub fn note_failure(&mut self, id: &NodeId) { if let Some(node) = self.nodes.get_mut(id) { - node.failures += 1; + node.last_contact = Some(NodeContact::failure()); + } + } + + /// Set last contact as success for a node + pub fn note_success(&mut self, id: &NodeId) { + if let Some(node) = self.nodes.get_mut(id) { + node.last_contact = Some(NodeContact::success()); } } @@ -324,7 +389,7 @@ impl NodeTable { return; } path.push(NODES_FILE); - let node_ids = self.nodes(IpFilter::default()); + let node_ids = self.nodes(&IpFilter::default()); let nodes = node_ids.into_iter() .map(|id| self.nodes.get(&id).expect("self.nodes() only returns node IDs from self.nodes")) .take(MAX_NODES) @@ -363,7 +428,7 @@ impl NodeTable { Ok(table) => { table.nodes.into_iter() .filter_map(|n| n.into_node()) - .map(|n| (n.id.clone(), n)) + .map(|n| (n.id, n)) .collect() }, Err(e) => { @@ -396,19 +461,38 @@ mod json { pub nodes: Vec, } + #[derive(Serialize, Deserialize)] + pub enum NodeContact { + #[serde(rename = "success")] + Success(u64), + #[serde(rename = "failure")] + Failure(u64), + } + + impl NodeContact { + pub fn into_node_contact(self) -> super::NodeContact { + match self { + NodeContact::Success(s) => super::NodeContact::Success( + time::UNIX_EPOCH + Duration::from_secs(s) + ), + NodeContact::Failure(s) => super::NodeContact::Failure( + time::UNIX_EPOCH + Duration::from_secs(s) + ), + } + } + } + #[derive(Serialize, Deserialize)] pub struct Node { pub url: String, - pub attempts: u32, - pub failures: u32, + pub last_contact: Option, } impl Node { pub fn into_node(self) -> Option { match super::Node::from_str(&self.url) { Ok(mut node) => { - node.attempts = self.attempts; - node.failures = self.failures; + node.last_contact = self.last_contact.map(|c| c.into_node_contact()); Some(node) }, _ => None, @@ -418,10 +502,18 @@ mod json { impl<'a> From<&'a super::Node> for Node { fn from(node: &'a super::Node) -> Self { + let last_contact = node.last_contact.and_then(|c| { + match c { + super::NodeContact::Success(t) => + t.duration_since(time::UNIX_EPOCH).ok().map(|d| NodeContact::Success(d.as_secs())), + super::NodeContact::Failure(t) => + t.duration_since(time::UNIX_EPOCH).ok().map(|d| NodeContact::Failure(d.as_secs())), + } + }); + Node { url: format!("{}", node), - attempts: node.attempts, - failures: node.failures, + last_contact } } } @@ -442,11 +534,34 @@ mod tests { assert!(endpoint.is_ok()); let v4 = match endpoint.unwrap().address { SocketAddr::V4(v4address) => v4address, - _ => panic!("should ve v4 address") + _ => panic!("should be v4 address") }; assert_eq!(SocketAddrV4::new(Ipv4Addr::new(123, 99, 55, 44), 7770), v4); } + #[test] + fn endpoint_parse_empty_ip_string_returns_error() { + let endpoint = NodeEndpoint::from_str(""); + assert!(endpoint.is_err()); + assert_matches!(endpoint.unwrap_err().kind(), &ErrorKind::AddressParse); + } + + #[test] + fn endpoint_parse_invalid_ip_string_returns_error() { + let endpoint = NodeEndpoint::from_str("beef"); + assert!(endpoint.is_err()); + assert_matches!(endpoint.unwrap_err().kind(), &ErrorKind::AddressParse); + } + + #[test] + fn endpoint_parse_valid_ip_without_port_returns_error() { + let endpoint = NodeEndpoint::from_str("123.123.123.123"); + assert!(endpoint.is_err()); + assert_matches!(endpoint.unwrap_err().kind(), &ErrorKind::AddressParse); + let endpoint = NodeEndpoint::from_str("123.123.123.123:123"); + assert!(endpoint.is_ok()) + } + #[test] fn node_parse() { assert!(validate_node_url("enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@22.99.55.44:7770").is_none()); @@ -464,42 +579,65 @@ mod tests { } #[test] - fn table_failure_percentage_order() { + fn node_parse_fails_for_invalid_urls() { + let node = Node::from_str("foo"); + assert!(node.is_err()); + assert_matches!(node.unwrap_err().kind(), &ErrorKind::AddressParse); + + let node = Node::from_str("enode://foo@bar"); + assert!(node.is_err()); + assert_matches!(node.unwrap_err().kind(), &ErrorKind::AddressParse); + } + + #[test] + fn table_last_contact_order() { let node1 = Node::from_str("enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@22.99.55.44:7770").unwrap(); let node2 = Node::from_str("enode://b979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@22.99.55.44:7770").unwrap(); let node3 = Node::from_str("enode://c979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@22.99.55.44:7770").unwrap(); let node4 = Node::from_str("enode://d979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@22.99.55.44:7770").unwrap(); + let node5 = Node::from_str("enode://e979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@22.99.55.44:7770").unwrap(); + let node6 = Node::from_str("enode://f979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@22.99.55.44:7770").unwrap(); let id1 = H512::from_str("a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c").unwrap(); let id2 = H512::from_str("b979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c").unwrap(); let id3 = H512::from_str("c979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c").unwrap(); let id4 = H512::from_str("d979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c").unwrap(); + let id5 = H512::from_str("e979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c").unwrap(); + let id6 = H512::from_str("f979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c").unwrap(); let mut table = NodeTable::new(None); table.add_node(node1); table.add_node(node2); table.add_node(node3); table.add_node(node4); + table.add_node(node5); + table.add_node(node6); - // node 1 - failure percentage 100% - table.get_mut(&id1).unwrap().attempts = 2; - table.note_failure(&id1); + // failures - nodes 1 & 2 table.note_failure(&id1); - - // node2 - failure percentage 33% - table.get_mut(&id2).unwrap().attempts = 3; table.note_failure(&id2); - // node3 - failure percentage 0% - table.get_mut(&id3).unwrap().attempts = 1; + // success - nodes 3 & 4 + table.note_success(&id3); + table.note_success(&id4); + + // success - node 5 (old contact) + table.get_mut(&id5).unwrap().last_contact = Some(NodeContact::Success(time::UNIX_EPOCH)); + + // unknown - node 6 - // node4 - failure percentage 50% (default when no attempts) + let r = table.nodes(&IpFilter::default()); - let r = table.nodes(IpFilter::default()); + assert_eq!(r[0][..], id4[..]); // most recent success + assert_eq!(r[1][..], id3[..]); - assert_eq!(r[0][..], id3[..]); - assert_eq!(r[1][..], id2[..]); - assert_eq!(r[2][..], id4[..]); - assert_eq!(r[3][..], id1[..]); + // unknown (old contacts and new nodes), randomly shuffled + assert!( + r[2][..] == id5[..] && r[3][..] == id6[..] || + r[2][..] == id6[..] && r[3][..] == id5[..] + ); + + assert_eq!(r[4][..], id1[..]); // oldest failure + assert_eq!(r[5][..], id2[..]); } #[test] @@ -507,23 +645,27 @@ mod tests { let tempdir = TempDir::new("").unwrap(); let node1 = Node::from_str("enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@22.99.55.44:7770").unwrap(); let node2 = Node::from_str("enode://b979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@22.99.55.44:7770").unwrap(); + let node3 = Node::from_str("enode://c979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@22.99.55.44:7770").unwrap(); let id1 = H512::from_str("a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c").unwrap(); let id2 = H512::from_str("b979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c").unwrap(); + let id3 = H512::from_str("c979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c").unwrap(); + { let mut table = NodeTable::new(Some(tempdir.path().to_str().unwrap().to_owned())); table.add_node(node1); table.add_node(node2); + table.add_node(node3); - table.get_mut(&id1).unwrap().attempts = 1; - table.get_mut(&id2).unwrap().attempts = 1; - table.note_failure(&id2); + table.note_success(&id2); + table.note_failure(&id3); } { let table = NodeTable::new(Some(tempdir.path().to_str().unwrap().to_owned())); - let r = table.nodes(IpFilter::default()); - assert_eq!(r[0][..], id1[..]); - assert_eq!(r[1][..], id2[..]); + let r = table.nodes(&IpFilter::default()); + assert_eq!(r[0][..], id2[..]); // latest success + assert_eq!(r[1][..], id1[..]); // unknown + assert_eq!(r[2][..], id3[..]); // oldest failure } } diff --git a/util/network-devp2p/src/service.rs b/util/network-devp2p/src/service.rs index 95867f8eb0abc5efec984f0ff59e31099be52178..d7182f46180e6685f40393aadd38121f8693459b 100644 --- a/util/network-devp2p/src/service.rs +++ b/util/network-devp2p/src/service.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -17,12 +17,13 @@ use network::{Error, NetworkConfiguration, NetworkProtocolHandler, NonReservedPeerMode}; use network::{NetworkContext, PeerId, ProtocolId, NetworkIoMessage}; use host::Host; -use stats::NetworkStats; use io::*; use parking_lot::RwLock; +use std::net::SocketAddr; +use std::ops::Range; use std::sync::Arc; use ansi_term::Colour; -use connection_filter::ConnectionFilter; +use network::ConnectionFilter; struct HostHandler { public_url: RwLock> @@ -33,7 +34,7 @@ impl IoHandler for HostHandler { if let NetworkIoMessage::NetworkStarted(ref public_url) = *message { let mut url = self.public_url.write(); if url.as_ref().map_or(true, |uref| uref != public_url) { - info!(target: "network", "Public node URL: {}", Colour::White.bold().paint(public_url.as_ref())); + info!(target: "network", "Public node URL: {}", Colour::White.bold().paint(AsRef::::as_ref(public_url))); } *url = Some(public_url.to_owned()); } @@ -46,7 +47,6 @@ pub struct NetworkService { io_service: IoService, host_info: String, host: RwLock>>, - stats: Arc, host_handler: Arc, config: NetworkConfiguration, filter: Option>, @@ -58,11 +58,9 @@ impl NetworkService { let host_handler = Arc::new(HostHandler { public_url: RwLock::new(None) }); let io_service = IoService::::start()?; - let stats = Arc::new(NetworkStats::new()); Ok(NetworkService { io_service: io_service, host_info: config.client_version.clone(), - stats: stats, host: RwLock::new(None), config: config, host_handler: host_handler, @@ -71,12 +69,17 @@ impl NetworkService { } /// Regiter a new protocol handler with the event loop. - pub fn register_protocol(&self, handler: Arc, protocol: ProtocolId, packet_count: u8, versions: &[u8]) -> Result<(), Error> { + pub fn register_protocol( + &self, + handler: Arc, + protocol: ProtocolId, + // version id + packet count + versions: &[(u8, u8)] + ) -> Result<(), Error> { self.io_service.send_message(NetworkIoMessage::AddHandler { - handler: handler, - protocol: protocol, + handler, + protocol, versions: versions.to_vec(), - packet_count: packet_count, })?; Ok(()) } @@ -91,14 +94,13 @@ impl NetworkService { &self.io_service } - /// Returns network statistics. - pub fn stats(&self) -> &NetworkStats { - &self.stats - } - - /// Returns network configuration. - pub fn config(&self) -> &NetworkConfiguration { - &self.config + /// Returns the number of peers allowed. + /// + /// Keep in mind that `range.end` is *exclusive*. + pub fn num_peers_range(&self) -> Range { + let start = self.config.min_peers; + let end = self.config.max_peers + 1; + start .. end } /// Returns external url if available. @@ -113,31 +115,36 @@ impl NetworkService { host.as_ref().map(|h| h.local_url()) } - /// Start network IO - pub fn start(&self) -> Result<(), Error> { + /// Start network IO. + /// + /// In case of error, also returns the listening address for better error reporting. + pub fn start(&self) -> Result<(), (Error, Option)> { let mut host = self.host.write(); + let listen_addr = self.config.listen_address.clone(); if host.is_none() { - let h = Arc::new(Host::new(self.config.clone(), self.stats.clone(), self.filter.clone())?); - self.io_service.register_handler(h.clone())?; + let h = Arc::new(Host::new(self.config.clone(), self.filter.clone()) + .map_err(|err| (err.into(), listen_addr))?); + self.io_service.register_handler(h.clone()) + .map_err(|err| (err.into(), listen_addr))?; *host = Some(h); } if self.host_handler.public_url.read().is_none() { - self.io_service.register_handler(self.host_handler.clone())?; + self.io_service.register_handler(self.host_handler.clone()) + .map_err(|err| (err.into(), listen_addr))?; } Ok(()) } - /// Stop network IO - pub fn stop(&self) -> Result<(), Error> { + /// Stop network IO. + pub fn stop(&self) { let mut host = self.host.write(); if let Some(ref host) = *host { let io = IoContext::new(self.io_service.channel(), 0); //TODO: take token id from host - host.stop(&io)?; + host.stop(&io); } *host = None; - Ok(()) } /// Get a list of all connected peers by id. @@ -170,7 +177,7 @@ impl NetworkService { let host = self.host.read(); if let Some(ref host) = *host { let io_ctxt = IoContext::new(self.io_service.channel(), 0); - host.set_non_reserved_mode(mode, &io_ctxt); + host.set_non_reserved_mode(&mode, &io_ctxt); } } diff --git a/util/network-devp2p/src/session.rs b/util/network-devp2p/src/session.rs index 39c96c063e09865b533ba0c5d1df282c08d203ff..a405ad469ddb2b2b1da2ce738016493ef97748cc 100644 --- a/util/network-devp2p/src/session.rs +++ b/util/network-devp2p/src/session.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -16,7 +16,6 @@ use std::{str, io}; use std::net::SocketAddr; -use std::sync::*; use std::collections::HashMap; use std::time::{Duration, Instant}; @@ -24,20 +23,19 @@ use mio::*; use mio::deprecated::{Handler, EventLoop}; use mio::tcp::*; use ethereum_types::H256; -use rlp::*; +use rlp::{Rlp, RlpStream, EMPTY_LIST_RLP}; use connection::{EncryptedConnection, Packet, Connection, MAX_PAYLOAD_SIZE}; use handshake::Handshake; use io::{IoContext, StreamToken}; use network::{Error, ErrorKind, DisconnectReason, SessionInfo, ProtocolId, PeerCapabilityInfo}; -use network::{SessionCapabilityInfo, HostInfo as HostInfoTrait}; +use network::SessionCapabilityInfo; use host::*; use node_table::NodeId; -use stats::NetworkStats; use snappy; // Timeout must be less than (interval - 1). -const PING_TIMEOUT_SEC: u64 = 60; -const PING_INTERVAL_SEC: u64 = 120; +const PING_TIMEOUT: Duration = Duration::from_secs(60); +const PING_INTERVAL: Duration = Duration::from_secs(120); const MIN_PROTOCOL_VERSION: u32 = 4; const MIN_COMPRESSION_PROTOCOL_VERSION: u32 = 5; @@ -103,10 +101,10 @@ impl Session { /// Create a new session out of comepleted handshake. This clones the handshake connection object /// and leaves the handhsake in limbo to be deregistered from the event loop. pub fn new(io: &IoContext, socket: TcpStream, token: StreamToken, id: Option<&NodeId>, - nonce: &H256, stats: Arc, host: &HostInfo) -> Result + nonce: &H256, host: &HostInfo) -> Result where Message: Send + Clone + Sync + 'static { let originated = id.is_some(); - let mut handshake = Handshake::new(token, id, socket, nonce, stats).expect("Can't create handshake"); + let mut handshake = Handshake::new(token, id, socket, nonce).expect("Can't create handshake"); let local_addr = handshake.connection.local_addr_str(); handshake.start(io, host, originated)?; Ok(Session { @@ -118,8 +116,8 @@ impl Session { protocol_version: 0, capabilities: Vec::new(), peer_capabilities: Vec::new(), - ping_ms: None, - originated: originated, + ping: None, + originated, remote_address: "Handshake".to_owned(), local_address: local_addr, }, @@ -133,7 +131,7 @@ impl Session { fn complete_handshake(&mut self, io: &IoContext, host: &HostInfo) -> Result<(), Error> where Message: Send + Sync + Clone { let connection = if let State::Handshake(ref mut h) = self.state { - self.info.id = Some(h.id.clone()); + self.info.id = Some(h.id); self.info.remote_address = h.connection.remote_addr_str(); EncryptedConnection::new(h)? } else { @@ -206,7 +204,7 @@ impl Session { } } if let Some(data) = packet_data { - return Ok(self.read_packet(io, data, host)?); + return Ok(self.read_packet(io, &data, host)?); } if create_session { self.complete_handshake(io, host)?; @@ -279,7 +277,7 @@ impl Session { None => packet_id }; let mut rlp = RlpStream::new(); - rlp.append(&(pid as u32)); + rlp.append(&(u32::from(pid))); let mut compressed = Vec::new(); let mut payload = data; // create a reference with local lifetime if self.compression { @@ -300,12 +298,12 @@ impl Session { return true; } let timed_out = if let Some(pong) = self.pong_time { - pong.duration_since(self.ping_time) > Duration::from_secs(PING_TIMEOUT_SEC) + pong.duration_since(self.ping_time) > PING_TIMEOUT } else { - self.ping_time.elapsed() > Duration::from_secs(PING_TIMEOUT_SEC) + self.ping_time.elapsed() > PING_TIMEOUT }; - if !timed_out && self.ping_time.elapsed() > Duration::from_secs(PING_INTERVAL_SEC) { + if !timed_out && self.ping_time.elapsed() > PING_INTERVAL { if let Err(e) = self.send_ping(io) { debug!("Error sending ping message: {:?}", e); } @@ -331,7 +329,7 @@ impl Session { } } - fn read_packet(&mut self, io: &IoContext, packet: Packet, host: &HostInfo) -> Result + fn read_packet(&mut self, io: &IoContext, packet: &Packet, host: &HostInfo) -> Result where Message: Send + Sync + Clone { if packet.data.len() < 2 { return Err(ErrorKind::BadProtocol.into()); @@ -351,12 +349,12 @@ impl Session { }; match packet_id { PACKET_HELLO => { - let rlp = UntrustedRlp::new(&data); //TODO: validate rlp expected size + let rlp = Rlp::new(&data); //TODO: validate rlp expected size self.read_hello(io, &rlp, host)?; Ok(SessionData::Ready) }, PACKET_DISCONNECT => { - let rlp = UntrustedRlp::new(&data); + let rlp = Rlp::new(&data); let reason: u8 = rlp.val_at(0)?; if self.had_hello { debug!(target:"network", "Disconnected: {}: {:?}", self.token(), DisconnectReason::from_u8(reason)); @@ -370,9 +368,7 @@ impl Session { PACKET_PONG => { let time = Instant::now(); self.pong_time = Some(time); - let ping_elapsed = time.duration_since(self.ping_time); - self.info.ping_ms = Some(ping_elapsed.as_secs() * 1_000 + - ping_elapsed.subsec_nanos() as u64 / 1_000_000); + self.info.ping = Some(time.duration_since(self.ping_time)); Ok(SessionData::Continue) }, PACKET_GET_PEERS => Ok(SessionData::None), //TODO; @@ -394,7 +390,7 @@ impl Session { match *self.protocol_states.entry(protocol).or_insert_with(|| ProtocolState::Pending(Vec::new())) { ProtocolState::Connected => { trace!(target: "network", "Packet {} mapped to {:?}:{}, i={}, capabilities={:?}", packet_id, protocol, protocol_packet_id, i, self.info.capabilities); - Ok(SessionData::Packet { data: data, protocol: protocol, packet_id: protocol_packet_id } ) + Ok(SessionData::Packet { data, protocol, packet_id: protocol_packet_id } ) } ProtocolState::Pending(ref mut pending) => { trace!(target: "network", "Packet {} deferred until protocol connection event completion", packet_id); @@ -423,7 +419,7 @@ impl Session { self.send(io, &rlp.drain()) } - fn read_hello(&mut self, io: &IoContext, rlp: &UntrustedRlp, host: &HostInfo) -> Result<(), Error> + fn read_hello(&mut self, io: &IoContext, rlp: &Rlp, host: &HostInfo) -> Result<(), Error> where Message: Send + Sync + Clone { let protocol = rlp.val_at::(0)?; let client_version = rlp.val_at::(1)?; @@ -472,11 +468,11 @@ impl Session { self.info.peer_capabilities = peer_caps; if self.info.capabilities.is_empty() { trace!(target: "network", "No common capabilities with peer."); - return Err(From::from(self.disconnect(io, DisconnectReason::UselessPeer))); + return Err(self.disconnect(io, DisconnectReason::UselessPeer)); } if protocol < MIN_PROTOCOL_VERSION { trace!(target: "network", "Peer protocol version mismatch: {}", protocol); - return Err(From::from(self.disconnect(io, DisconnectReason::UselessPeer))); + return Err(self.disconnect(io, DisconnectReason::UselessPeer)); } self.compression = protocol >= MIN_COMPRESSION_PROTOCOL_VERSION; self.send_ping(io)?; @@ -519,4 +515,3 @@ impl Session { Ok(()) } } - diff --git a/util/network-devp2p/src/stats.rs b/util/network-devp2p/src/stats.rs deleted file mode 100644 index d4dbfe04329fb5d3b0c1dcab62fecf2038be3470..0000000000000000000000000000000000000000 --- a/util/network-devp2p/src/stats.rs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2015-2017 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 . - -//! Network Statistics -use std::sync::atomic::*; - -/// Network statistics structure -#[derive(Default, Debug)] -pub struct NetworkStats { - /// Bytes received - recv: AtomicUsize, - /// Bytes sent - send: AtomicUsize, - /// Total number of sessions created - sessions: AtomicUsize, -} - -impl NetworkStats { - /// Increase bytes received. - #[inline] - pub fn inc_recv(&self, size: usize) { - self.recv.fetch_add(size, Ordering::Relaxed); - } - - /// Increase bytes sent. - #[inline] - pub fn inc_send(&self, size: usize) { - self.send.fetch_add(size, Ordering::Relaxed); - } - - /// Increase number of sessions. - #[inline] - pub fn inc_sessions(&self) { - self.sessions.fetch_add(1, Ordering::Relaxed); - } - - /// Get bytes sent. - #[inline] - pub fn send(&self) -> usize { - self.send.load(Ordering::Relaxed) - } - - /// Get bytes received. - #[inline] - pub fn recv(&self) -> usize { - self.recv.load(Ordering::Relaxed) - } - - /// Get total number of sessions created. - #[inline] - pub fn sessions(&self) -> usize { - self.sessions.load(Ordering::Relaxed) - } - - /// Create a new empty instance. - pub fn new() -> NetworkStats { - NetworkStats { - recv: AtomicUsize::new(0), - send: AtomicUsize::new(0), - sessions: AtomicUsize::new(0), - } - } -} diff --git a/util/network-devp2p/tests/tests.rs b/util/network-devp2p/tests/tests.rs index e964a9a8b50ce210076aa0b6bc311ee60b05f86c..970aa3b8a589f8a6e995f273f46e19117a5a7c79 100644 --- a/util/network-devp2p/tests/tests.rs +++ b/util/network-devp2p/tests/tests.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -14,8 +14,6 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -#[macro_use] -extern crate log; extern crate parking_lot; extern crate ethcore_bytes; extern crate ethcore_io as io; @@ -54,7 +52,7 @@ impl TestProtocol { /// Creates and register protocol with the network service pub fn register(service: &mut NetworkService, drop_session: bool) -> Arc { let handler = Arc::new(TestProtocol::new(drop_session)); - service.register_protocol(handler.clone(), *b"tst", 1, &[42u8, 43u8]).expect("Error registering test protocol handler"); + service.register_protocol(handler.clone(), *b"tst", &[(42u8, 1u8), (43u8, 1u8)]).expect("Error registering test protocol handler"); handler } @@ -72,8 +70,8 @@ impl TestProtocol { } impl NetworkProtocolHandler for TestProtocol { - fn initialize(&self, io: &NetworkContext, _host_info: &HostInfo) { - io.register_timer(0, 10).unwrap(); + fn initialize(&self, io: &NetworkContext) { + io.register_timer(0, Duration::from_millis(10)).unwrap(); } fn read(&self, _io: &NetworkContext, _peer: &PeerId, packet_id: u8, data: &[u8]) { @@ -101,35 +99,11 @@ impl NetworkProtocolHandler for TestProtocol { } } - #[test] fn net_service() { let service = NetworkService::new(NetworkConfiguration::new_local(), None).expect("Error creating network service"); service.start().unwrap(); - service.register_protocol(Arc::new(TestProtocol::new(false)), *b"myp", 1, &[1u8]).unwrap(); -} - -#[test] -fn net_connect() { - ::ethcore_logger::init_log(); - let key1 = Random.generate().unwrap(); - let mut config1 = NetworkConfiguration::new_local(); - config1.use_secret = Some(key1.secret().clone()); - config1.boot_nodes = vec![ ]; - let mut service1 = NetworkService::new(config1, None).unwrap(); - service1.start().unwrap(); - let handler1 = TestProtocol::register(&mut service1, false); - let mut config2 = NetworkConfiguration::new_local(); - info!("net_connect: local URL: {}", service1.local_url().unwrap()); - config2.boot_nodes = vec![ service1.local_url().unwrap() ]; - let mut service2 = NetworkService::new(config2, None).unwrap(); - service2.start().unwrap(); - let handler2 = TestProtocol::register(&mut service2, false); - while !handler1.got_packet() && !handler2.got_packet() && (service1.stats().sessions() == 0 || service2.stats().sessions() == 0) { - thread::sleep(Duration::from_millis(50)); - } - assert!(service1.stats().sessions() >= 1); - assert!(service2.stats().sessions() >= 1); + service.register_protocol(Arc::new(TestProtocol::new(false)), *b"myp", &[(1u8, 1u8)]).unwrap(); } #[test] @@ -137,7 +111,7 @@ fn net_start_stop() { let config = NetworkConfiguration::new_local(); let service = NetworkService::new(config, None).unwrap(); service.start().unwrap(); - service.stop().unwrap(); + service.stop(); service.start().unwrap(); } diff --git a/util/network/Cargo.toml b/util/network/Cargo.toml index 7f9b30400b5aac60b62a54013ae9d9e056f1de51..4ae699098ddd3b77c492d221fc001e36e1b6931d 100644 --- a/util/network/Cargo.toml +++ b/util/network/Cargo.toml @@ -3,15 +3,20 @@ description = "Ethcore network library" homepage = "http://parity.io" license = "GPL-3.0" name = "ethcore-network" -version = "1.11.0" +version = "1.12.0" authors = ["Parity Technologies "] [dependencies] +error-chain = { version = "0.11", default-features = false } +ethcore-crypto = { path = "../../ethcore/crypto" } ethcore-io = { path = "../io" } -ethereum-types = "0.2" +ethereum-types = "0.3" ethkey = { path = "../../ethkey" } -ethcrypto = { path = "../../ethcrypto" } -rlp = { path = "../rlp" } ipnetwork = "0.12.6" +rlp = { path = "../rlp" } +libc = "0.2" snappy = { git = "https://github.com/paritytech/rust-snappy" } -error-chain = { version = "0.11", default-features = false } + + +[dev-dependencies] +assert_matches = "1.2" diff --git a/util/network-devp2p/src/connection_filter.rs b/util/network/src/connection_filter.rs similarity index 95% rename from util/network-devp2p/src/connection_filter.rs rename to util/network/src/connection_filter.rs index 5afe5865b7313643b56b8f4d438d887c57a1eeb8..e146aee4c7c6aed553674c3c5ae96c51ba7e0302 100644 --- a/util/network-devp2p/src/connection_filter.rs +++ b/util/network/src/connection_filter.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/util/network/src/error.rs b/util/network/src/error.rs index 48bcf75965635d56ba568d7828d1c1b27574addf..4233b9e058b24d727dbeb0b43518b14cd310cde5 100644 --- a/util/network/src/error.rs +++ b/util/network/src/error.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -15,6 +15,7 @@ // along with Parity. If not, see . use std::{io, net, fmt}; +use libc::{ENFILE, EMFILE}; use io::IoError; use {rlp, ethkey, crypto, snappy}; @@ -83,12 +84,16 @@ impl fmt::Display for DisconnectReason { error_chain! { foreign_links { SocketIo(IoError) #[doc = "Socket IO error."]; - Io(io::Error) #[doc = "Error concerning the Rust standard library's IO subsystem."]; - AddressParse(net::AddrParseError) #[doc = "Error concerning the network address parsing subsystem."]; Decompression(snappy::InvalidInput) #[doc = "Decompression error."]; } errors { + #[doc = "Error concerning the network address parsing subsystem."] + AddressParse { + description("Failed to parse network address"), + display("Failed to parse network address"), + } + #[doc = "Error concerning the network address resolution subsystem."] AddressResolve(err: Option) { description("Failed to resolve network address"), @@ -136,6 +141,34 @@ error_chain! { description("Packet is too large"), display("Packet is too large"), } + + #[doc = "Reached system resource limits for this process"] + ProcessTooManyFiles { + description("Too many open files in process."), + display("Too many open files in this process. Check your resource limits and restart parity"), + } + + #[doc = "Reached system wide resource limits"] + SystemTooManyFiles { + description("Too many open files on system."), + display("Too many open files on system. Consider closing some processes/release some file handlers or increas the system-wide resource limits and restart parity."), + } + + #[doc = "An unknown IO error occurred."] + Io(err: io::Error) { + description("IO Error"), + display("Unexpected IO error: {}", err), + } + } +} + +impl From for Error { + fn from(err: io::Error) -> Self { + match err.raw_os_error() { + Some(ENFILE) => ErrorKind::ProcessTooManyFiles.into(), + Some(EMFILE) => ErrorKind::SystemTooManyFiles.into(), + _ => Error::from_kind(ErrorKind::Io(err)) + } } } @@ -151,12 +184,22 @@ impl From for Error { } } -impl From for Error { - fn from(_err: crypto::Error) -> Self { +impl From for Error { + fn from(_err: ethkey::crypto::Error) -> Self { + ErrorKind::Auth.into() + } +} + +impl From for Error { + fn from(_err: crypto::error::SymmError) -> Self { ErrorKind::Auth.into() } } +impl From for Error { + fn from(_err: net::AddrParseError) -> Self { ErrorKind::AddressParse.into() } +} + #[test] fn test_errors() { assert_eq!(DisconnectReason::ClientQuit, DisconnectReason::from_u8(8)); @@ -168,11 +211,34 @@ fn test_errors() { match *>::from(rlp::DecoderError::RlpIsTooBig).kind() { ErrorKind::Auth => {}, - _ => panic!("Unexpeceted error"), + _ => panic!("Unexpected error"), } - match *>::from(crypto::Error::InvalidMessage).kind() { + match *>::from(ethkey::crypto::Error::InvalidMessage).kind() { ErrorKind::Auth => {}, - _ => panic!("Unexpeceted error"), + _ => panic!("Unexpected error"), } } + +#[test] +fn test_io_errors() { + use libc::{EMFILE, ENFILE}; + + assert_matches!( + >::from( + io::Error::from_raw_os_error(ENFILE) + ).kind(), + ErrorKind::ProcessTooManyFiles); + + assert_matches!( + >::from( + io::Error::from_raw_os_error(EMFILE) + ).kind(), + ErrorKind::SystemTooManyFiles); + + assert_matches!( + >::from( + io::Error::from_raw_os_error(0) + ).kind(), + ErrorKind::Io(_)); +} diff --git a/util/network/src/lib.rs b/util/network/src/lib.rs index 7bd13d1f392dac3633bfbaf1b74007aadcf322c7..9b7328bdbdb4362d0f4d345674a6eaf71548de08 100644 --- a/util/network/src/lib.rs +++ b/util/network/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -16,19 +16,25 @@ #![recursion_limit="128"] +extern crate ethcore_crypto as crypto; extern crate ethcore_io as io; -extern crate ethcrypto as crypto; extern crate ethereum_types; extern crate ethkey; extern crate rlp; extern crate ipnetwork; extern crate snappy; +extern crate libc; + +#[cfg(test)] #[macro_use] +extern crate assert_matches; #[macro_use] extern crate error_chain; +mod connection_filter; mod error; +pub use connection_filter::{ConnectionFilter, ConnectionDirection}; pub use io::TimerToken; pub use error::{Error, ErrorKind, DisconnectReason}; @@ -37,11 +43,11 @@ use std::collections::HashMap; use std::net::{SocketAddr, SocketAddrV4, Ipv4Addr}; use std::str::{self, FromStr}; use std::sync::Arc; +use std::time::Duration; use ipnetwork::{IpNetwork, IpNetworkError}; -use io::IoChannel; use ethkey::Secret; -use ethereum_types::{H256, H512}; -use rlp::{Decodable, DecoderError, UntrustedRlp}; +use ethereum_types::H512; +use rlp::{Decodable, DecoderError, Rlp}; /// Protocol handler level packet id pub type PacketId = u8; @@ -63,10 +69,8 @@ pub enum NetworkIoMessage { handler: Arc, /// Protocol Id. protocol: ProtocolId, - /// Supported protocol versions. - versions: Vec, - /// Number of packet IDs reserved by the protocol. - packet_count: u8, + /// Supported protocol versions and number of packet IDs reserved by the protocol (packet count). + versions: Vec<(u8, u8)>, }, /// Register a new protocol timer AddTimer { @@ -74,8 +78,8 @@ pub enum NetworkIoMessage { protocol: ProtocolId, /// Timer token. token: TimerToken, - /// Timer delay in milliseconds. - delay: u64, + /// Timer delay. + delay: Duration, }, /// Initliaze public interface. InitPublicInterface, @@ -100,8 +104,8 @@ pub struct SessionInfo { pub capabilities: Vec, /// Peer protocol capabilities pub peer_capabilities: Vec, - /// Peer ping delay in milliseconds - pub ping_ms: Option, + /// Peer ping delay + pub ping: Option, /// True if this session was originated by us. pub originated: bool, /// Remote endpoint address of the session @@ -117,7 +121,7 @@ pub struct PeerCapabilityInfo { } impl Decodable for PeerCapabilityInfo { - fn decode(rlp: &UntrustedRlp) -> Result { + fn decode(rlp: &Rlp) -> Result { let p: Vec = rlp.val_at(0)?; if p.len() != 3 { return Err(DecoderError::Custom("Invalid subprotocol string length. Should be 3")); @@ -258,9 +262,6 @@ pub trait NetworkContext { /// Respond to a current network message. Panics if no there is no packet in the context. If the session is expired returns nothing. fn respond(&self, packet_id: PacketId, data: Vec) -> Result<(), Error>; - /// Get an IoChannel. - fn io_channel(&self) -> IoChannel; - /// Disconnect a peer and prevent it from connecting again. fn disable_peer(&self, peer: PeerId); @@ -271,7 +272,7 @@ pub trait NetworkContext { fn is_expired(&self) -> bool; /// Register a new IO timer. 'IoHandler::timeout' will be called with the token. - fn register_timer(&self, token: TimerToken, ms: u64) -> Result<(), Error>; + fn register_timer(&self, token: TimerToken, delay: Duration) -> Result<(), Error>; /// Returns peer identification string fn peer_client_version(&self, peer: PeerId) -> String; @@ -299,10 +300,6 @@ impl<'a, T> NetworkContext for &'a T where T: ?Sized + NetworkContext { (**self).respond(packet_id, data) } - fn io_channel(&self) -> IoChannel { - (**self).io_channel() - } - fn disable_peer(&self, peer: PeerId) { (**self).disable_peer(peer) } @@ -315,8 +312,8 @@ impl<'a, T> NetworkContext for &'a T where T: ?Sized + NetworkContext { (**self).is_expired() } - fn register_timer(&self, token: TimerToken, ms: u64) -> Result<(), Error> { - (**self).register_timer(token, ms) + fn register_timer(&self, token: TimerToken, delay: Duration) -> Result<(), Error> { + (**self).register_timer(token, delay) } fn peer_client_version(&self, peer: PeerId) -> String { @@ -336,23 +333,12 @@ impl<'a, T> NetworkContext for &'a T where T: ?Sized + NetworkContext { } } -pub trait HostInfo { - /// Returns public key - fn id(&self) -> &NodeId; - /// Returns secret key - fn secret(&self) -> &Secret; - /// Increments and returns connection nonce. - fn next_nonce(&mut self) -> H256; - /// Returns the client version. - fn client_version(&self) -> &str; -} - /// Network IO protocol handler. This needs to be implemented for each new subprotocol. /// All the handler function are called from within IO event loop. /// `Message` is the type for message data. pub trait NetworkProtocolHandler: Sync + Send { /// Initialize the handler - fn initialize(&self, _io: &NetworkContext, _host_info: &HostInfo) {} + fn initialize(&self, _io: &NetworkContext) {} /// Called when new network packet received. fn read(&self, io: &NetworkContext, peer: &PeerId, packet_id: u8, data: &[u8]); /// Called when new peer is connected. Only called when peer supports the same protocol. diff --git a/util/panic_hook/src/lib.rs b/util/panic_hook/src/lib.rs index 1136e9e362378fe97568b2727c1ad80f1f6f1d42..ef6220572ae04d225998f1e5ab5c45b2dc129e51 100644 --- a/util/panic_hook/src/lib.rs +++ b/util/panic_hook/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -8,7 +8,7 @@ // 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 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License diff --git a/util/path/src/lib.rs b/util/path/src/lib.rs index 761b51152219bedaa5f2ba77f8bd3dc8a4cafd18..38608db6604731a65e852a39adbed6e77d71356c 100644 --- a/util/path/src/lib.rs +++ b/util/path/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -98,4 +98,3 @@ pub fn restrict_permissions_owner(_file_path: &Path, _write: bool, _executable: //TODO: implement me Ok(()) } - diff --git a/util/patricia_trie/Cargo.toml b/util/patricia_trie/Cargo.toml index 318db6b9988ee9ddd7e9c3f9d877b728f93f7535..48b06b214637a73124bd7a26c3c12ffec64445b9 100644 --- a/util/patricia_trie/Cargo.toml +++ b/util/patricia_trie/Cargo.toml @@ -10,7 +10,7 @@ elastic-array = "0.10" log = "0.3" rand = "0.4" ethcore-bytes = { version = "0.1.0", path = "../bytes" } -ethereum-types = "0.2" +ethereum-types = "0.3" keccak-hash = { version = "0.1.0", path = "../hash" } hashdb = { version = "0.1.1", path = "../hashdb" } rlp = { version = "0.2.1", path = "../rlp" } diff --git a/util/patricia_trie/src/fatdb.rs b/util/patricia_trie/src/fatdb.rs index d428ff8116b18bbaf83f9c01a67d09969b7ab4f1..90cdef90214f06c02c6595c637d78f11520a3739 100644 --- a/util/patricia_trie/src/fatdb.rs +++ b/util/patricia_trie/src/fatdb.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/util/patricia_trie/src/fatdbmut.rs b/util/patricia_trie/src/fatdbmut.rs index 4b7f2de0632acc049528c309834ae848da66d814..9bf7b88036962c58f5f0f91e1c826c8b8cc41969 100644 --- a/util/patricia_trie/src/fatdbmut.rs +++ b/util/patricia_trie/src/fatdbmut.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/util/patricia_trie/src/lib.rs b/util/patricia_trie/src/lib.rs index 3a1d683c26cf6812ae2b1f99815d15cf5bfceef2..8e0e44f032fd0fc2a444e14fabb3e133d3b6a9d2 100644 --- a/util/patricia_trie/src/lib.rs +++ b/util/patricia_trie/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -67,6 +67,8 @@ pub enum TrieError { InvalidStateRoot(H256), /// Trie item not found in the database, IncompleteDatabase(H256), + /// Corrupt Trie item + DecoderError(rlp::DecoderError), } impl fmt::Display for TrieError { @@ -75,6 +77,7 @@ impl fmt::Display for TrieError { TrieError::InvalidStateRoot(ref root) => write!(f, "Invalid state root: {}", root), TrieError::IncompleteDatabase(ref missing) => write!(f, "Database missing expected key: {}", missing), + TrieError::DecoderError(ref err) => write!(f, "Decoding failed with {}", err), } } } @@ -84,10 +87,15 @@ impl error::Error for TrieError { match *self { TrieError::InvalidStateRoot(_) => "Invalid state root", TrieError::IncompleteDatabase(_) => "Incomplete database", + TrieError::DecoderError(ref e) => e.description(), } } } +impl From for Box { + fn from(e: rlp::DecoderError) -> Self { Box::new(TrieError::DecoderError(e)) } +} + /// Trie result type. Boxed to avoid copying around extra space for `H256`s on successful queries. pub type Result = ::std::result::Result>; diff --git a/util/patricia_trie/src/lookup.rs b/util/patricia_trie/src/lookup.rs index 88d2bc66e02c962688d12116b13ca90a403e24de..ae91689a3118321a067f8ef1f3377272826203df 100644 --- a/util/patricia_trie/src/lookup.rs +++ b/util/patricia_trie/src/lookup.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -55,7 +55,7 @@ impl<'a, Q: Query> Lookup<'a, Q> { // without incrementing the depth. let mut node_data = &node_data[..]; loop { - match Node::decoded(node_data).expect("rlp read from db; qed") { + match Node::decoded(node_data)? { Node::Leaf(slice, value) => { return Ok(match slice == key { true => Some(self.query.decode(value)), diff --git a/util/patricia_trie/src/nibbleslice.rs b/util/patricia_trie/src/nibbleslice.rs index c2dd6611e21326842651241a73d33bf3740f9ef7..8cb03e53dd664b1c5ce95d4d31016c9dc038b97b 100644 --- a/util/patricia_trie/src/nibbleslice.rs +++ b/util/patricia_trie/src/nibbleslice.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -67,31 +67,29 @@ impl<'a> Iterator for NibbleSliceIterator<'a> { } } -impl<'a, 'view> NibbleSlice<'a> where 'a: 'view { +impl<'a> NibbleSlice<'a> { /// Create a new nibble slice with the given byte-slice. pub fn new(data: &'a [u8]) -> Self { NibbleSlice::new_offset(data, 0) } /// Create a new nibble slice with the given byte-slice with a nibble offset. - pub fn new_offset(data: &'a [u8], offset: usize) -> Self { NibbleSlice{data: data, offset: offset, data_encode_suffix: &b""[..], offset_encode_suffix: 0} } + pub fn new_offset(data: &'a [u8], offset: usize) -> Self { + NibbleSlice { + data, + offset, + data_encode_suffix: &b""[..], + offset_encode_suffix: 0 + } + } /// Create a composed nibble slice; one followed by the other. - pub fn new_composed(a: &'a NibbleSlice, b: &'a NibbleSlice) -> Self { NibbleSlice{data: a.data, offset: a.offset, data_encode_suffix: b.data, offset_encode_suffix: b.offset} } - - /*pub fn new_composed_bytes_offset(a: &NibbleSlice, b: &NibbleSlice) -> (Bytes, usize) { - let r: Vec::with_capacity((a.len() + b.len() + 1) / 2); - let mut i = (a.len() + b.len()) % 2; - while i < a.len() { - match i % 2 { - 0 => , - 1 => , - } - i += 1; + pub fn new_composed(a: &NibbleSlice<'a>, b: &NibbleSlice<'a>) -> Self { + NibbleSlice { + data: a.data, + offset: a.offset, + data_encode_suffix: b.data, + offset_encode_suffix: b.offset } - while i < a.len() + b.len() { - i += 1; - } - (r, a.len() + b.len()) - }*/ + } /// Get an iterator for the series of nibbles. pub fn iter(&'a self) -> NibbleSliceIterator<'a> { @@ -132,7 +130,14 @@ impl<'a, 'view> NibbleSlice<'a> where 'a: 'view { } /// Return object which represents a view on to this slice (further) offset by `i` nibbles. - pub fn mid(&'view self, i: usize) -> NibbleSlice<'a> { NibbleSlice{ data: self.data, offset: self.offset + i, data_encode_suffix: &b""[..], offset_encode_suffix: 0 } } + pub fn mid(&self, i: usize) -> NibbleSlice<'a> { + NibbleSlice { + data: self.data, + offset: self.offset + i, + data_encode_suffix: &b""[..], + offset_encode_suffix: 0 + } + } /// Do we start with the same nibbles as the whole of `them`? pub fn starts_with(&self, them: &Self) -> bool { self.common_prefix(them) == them.len() } diff --git a/util/patricia_trie/src/nibblevec.rs b/util/patricia_trie/src/nibblevec.rs index fbe97496ae84881846d1e79ecbc1aed83f9ca210..4398dbc6f7e2842976c648675387bccd66e9a3b2 100644 --- a/util/patricia_trie/src/nibblevec.rs +++ b/util/patricia_trie/src/nibblevec.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/util/patricia_trie/src/node.rs b/util/patricia_trie/src/node.rs index 47807940fee2a9eba86300f4a816956e8991e04a..0ded1f66dbc08b71763b62aac74e5a918c0b5d01 100644 --- a/util/patricia_trie/src/node.rs +++ b/util/patricia_trie/src/node.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -19,7 +19,7 @@ use elastic_array::ElasticArray36; use nibbleslice::NibbleSlice; use nibblevec::NibbleVec; use bytes::*; -use rlp::{UntrustedRlp, RlpStream, Prototype, DecoderError}; +use rlp::{Rlp, RlpStream, Prototype, DecoderError}; use hashdb::DBValue; /// Partial node key type. @@ -41,7 +41,7 @@ pub enum Node<'a> { impl<'a> Node<'a> { /// Decode the `node_rlp` and return the Node. pub fn decoded(node_rlp: &'a [u8]) -> Result { - let r = UntrustedRlp::new(node_rlp); + let r = Rlp::new(node_rlp); match r.prototype()? { // either leaf or extension - decode first item with NibbleSlice::??? // and use is_leaf return to figure out which. @@ -105,7 +105,7 @@ impl<'a> Node<'a> { } pub fn try_decode_hash(node_data: &[u8]) -> Option { - let r = UntrustedRlp::new(node_data); + let r = Rlp::new(node_data); if r.is_data() && r.size() == 32 { Some(r.as_val().expect("Hash is the correct size of 32 bytes; qed")) } else { diff --git a/util/patricia_trie/src/recorder.rs b/util/patricia_trie/src/recorder.rs index 35a515b704d52951a3636ad2c02f4a0e901230d9..6a0f9b45ebe2118e02315cc202de81abcad15119 100644 --- a/util/patricia_trie/src/recorder.rs +++ b/util/patricia_trie/src/recorder.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/util/patricia_trie/src/sectriedb.rs b/util/patricia_trie/src/sectriedb.rs index a9176d022ab357fdd496ebacf4e84e29e2689db6..c8d5ec0ec8f3dff309120d23ea293002bcada40e 100644 --- a/util/patricia_trie/src/sectriedb.rs +++ b/util/patricia_trie/src/sectriedb.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/util/patricia_trie/src/sectriedbmut.rs b/util/patricia_trie/src/sectriedbmut.rs index b0436b271f6490d0939295d2c15510bd507dfcda..335fb2f1835dd07044cdbe59c558c06023630073 100644 --- a/util/patricia_trie/src/sectriedbmut.rs +++ b/util/patricia_trie/src/sectriedbmut.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/util/patricia_trie/src/triedb.rs b/util/patricia_trie/src/triedb.rs index 682f12467d3dcb17fe4177bb0b670b5d02697e2c..65ce3caba8a8e6816fd7e076077396f128cd7513 100644 --- a/util/patricia_trie/src/triedb.rs +++ b/util/patricia_trie/src/triedb.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -21,7 +21,7 @@ use super::node::{Node, OwnedNode}; use super::lookup::Lookup; use super::{Trie, TrieItem, TrieError, TrieIterator, Query}; use ethereum_types::H256; -use bytes::{ToPretty, Bytes}; +use bytes::Bytes; /// A `Trie` implementation using a generic `HashDB` backing database. /// @@ -78,64 +78,9 @@ impl<'db> TrieDB<'db> { /// Get the data of the root node. fn root_data(&self) -> super::Result { - self.db.get(self.root).ok_or_else(|| Box::new(TrieError::InvalidStateRoot(*self.root))) - } - - /// Indentation helper for `format_all`. - fn fmt_indent(&self, f: &mut fmt::Formatter, size: usize) -> fmt::Result { - for _ in 0..size { - write!(f, " ")?; - } - Ok(()) - } - - /// Recursion helper for implementation of formatting trait. - fn fmt_all(&self, node: Node, f: &mut fmt::Formatter, deepness: usize) -> fmt::Result { - match node { - Node::Leaf(slice, value) => writeln!(f, "'{:?}: {:?}.", slice, value.pretty())?, - Node::Extension(ref slice, ref item) => { - write!(f, "'{:?} ", slice)?; - if let Ok(node) = self.get_raw_or_lookup(&*item) { - match Node::decoded(&node) { - Ok(n) => self.fmt_all(n, f, deepness)?, - Err(err) => writeln!(f, "ERROR decoding node extension Rlp: {}", err)?, - } - } - }, - Node::Branch(ref nodes, ref value) => { - writeln!(f, "")?; - if let Some(ref v) = *value { - self.fmt_indent(f, deepness + 1)?; - writeln!(f, "=: {:?}", v.pretty())? - } - for i in 0..16 { - let node = self.get_raw_or_lookup(&*nodes[i]); - match node.as_ref() { - Ok(n) => { - match Node::decoded(&*n) { - Ok(Node::Empty) => {}, - Ok(n) => { - self.fmt_indent(f, deepness + 1)?; - write!(f, "'{:x} ", i)?; - self.fmt_all(n, f, deepness + 1)?; - } - Err(e) => { - write!(f, "ERROR decoding node branch Rlp: {}", e)? - } - } - } - Err(e) => { - write!(f, "ERROR: {}", e)?; - } - } - } - }, - // empty - Node::Empty => { - writeln!(f, "")?; - } - }; - Ok(()) + self.db + .get(self.root) + .ok_or_else(|| Box::new(TrieError::InvalidStateRoot(*self.root))) } /// Given some node-describing data `node`, return the actual node RLP. @@ -174,15 +119,58 @@ impl<'db> Trie for TrieDB<'db> { } } +// This is for pretty debug output only +struct TrieAwareDebugNode<'db, 'a> { + trie: &'db TrieDB<'db>, + key: &'a[u8] +} + +impl<'db, 'a> fmt::Debug for TrieAwareDebugNode<'db, 'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if let Ok(node) = self.trie.get_raw_or_lookup(self.key) { + match Node::decoded(&node) { + Ok(Node::Leaf(slice, value)) => f.debug_struct("Node::Leaf") + .field("slice", &slice) + .field("value", &value) + .finish(), + Ok(Node::Extension(ref slice, ref item)) => f.debug_struct("Node::Extension") + .field("slice", &slice) + .field("item", &TrieAwareDebugNode{trie: self.trie, key: item}) + .finish(), + Ok(Node::Branch(ref nodes, ref value)) => { + let nodes: Vec = nodes.into_iter().map(|n| TrieAwareDebugNode{trie: self.trie, key: n} ).collect(); + f.debug_struct("Node::Branch") + .field("nodes", &nodes) + .field("value", &value) + .finish() + }, + Ok(Node::Empty) => f.debug_struct("Node::Empty").finish(), + + Err(e) => f.debug_struct("BROKEN_NODE") + .field("key", &self.key) + .field("error", &format!("ERROR decoding node branch Rlp: {}", e)) + .finish() + } + } else { + f.debug_struct("BROKEN_NODE") + .field("key", &self.key) + .field("error", &"Not found") + .finish() + } + } +} + + impl<'db> fmt::Debug for TrieDB<'db> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - writeln!(f, "c={:?} [", self.hash_count)?; let root_rlp = self.db.get(self.root).expect("Trie root not found!"); - match Node::decoded(&root_rlp) { - Ok(node) => self.fmt_all(node, f, 0)?, - Err(e) => writeln!(f, "ERROR decoding node rlp: {}", e)?, - } - writeln!(f, "]") + f.debug_struct("TrieDB") + .field("hash_count", &self.hash_count) + .field("root", &TrieAwareDebugNode { + trie: self, + key: &root_rlp + }) + .finish() } } @@ -493,3 +481,140 @@ fn get_len() { assert_eq!(t.get_with(b"B", |x: &[u8]| x.len()), Ok(Some(5))); assert_eq!(t.get_with(b"C", |x: &[u8]| x.len()), Ok(None)); } + + +#[test] +fn debug_output_supports_pretty_print() { + use memorydb::*; + use super::TrieMut; + use super::triedbmut::*; + + let d = vec![ DBValue::from_slice(b"A"), DBValue::from_slice(b"AA"), DBValue::from_slice(b"AB"), DBValue::from_slice(b"B") ]; + + let mut memdb = MemoryDB::new(); + let mut root = H256::new(); + let root = { + let mut t = TrieDBMut::new(&mut memdb, &mut root); + for x in &d { + t.insert(x, x).unwrap(); + } + t.root().clone() + }; + let t = TrieDB::new(&memdb, &root).unwrap(); + + assert_eq!(format!("{:?}", t), "TrieDB { hash_count: 0, root: Node::Extension { slice: 4, item: Node::Branch { nodes: [Node::Empty, Node::Branch { nodes: [Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Branch { nodes: [Node::Empty, Node::Leaf { slice: , value: [65, 65] }, Node::Leaf { slice: , value: [65, 66] }, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty], value: None }, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty], value: Some([65]) }, Node::Leaf { slice: , value: [66] }, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty, Node::Empty], value: None } } }"); + assert_eq!(format!("{:#?}", t), +"TrieDB { + hash_count: 0, + root: Node::Extension { + slice: 4, + item: Node::Branch { + nodes: [ + Node::Empty, + Node::Branch { + nodes: [ + Node::Empty, + Node::Empty, + Node::Empty, + Node::Empty, + Node::Branch { + nodes: [ + Node::Empty, + Node::Leaf { + slice: , + value: [ + 65, + 65 + ] + }, + Node::Leaf { + slice: , + value: [ + 65, + 66 + ] + }, + Node::Empty, + Node::Empty, + Node::Empty, + Node::Empty, + Node::Empty, + Node::Empty, + Node::Empty, + Node::Empty, + Node::Empty, + Node::Empty, + Node::Empty, + Node::Empty, + Node::Empty + ], + value: None + }, + Node::Empty, + Node::Empty, + Node::Empty, + Node::Empty, + Node::Empty, + Node::Empty, + Node::Empty, + Node::Empty, + Node::Empty, + Node::Empty, + Node::Empty + ], + value: Some( + [ + 65 + ] + ) + }, + Node::Leaf { + slice: , + value: [ + 66 + ] + }, + Node::Empty, + Node::Empty, + Node::Empty, + Node::Empty, + Node::Empty, + Node::Empty, + Node::Empty, + Node::Empty, + Node::Empty, + Node::Empty, + Node::Empty, + Node::Empty, + Node::Empty + ], + value: None + } + } +}"); +} + +#[test] +fn test_lookup_with_corrupt_data_returns_decoder_error() { + use memorydb::*; + use super::TrieMut; + use super::triedbmut::*; + use rlp; + use ethereum_types::H512; + + let mut memdb = MemoryDB::new(); + let mut root = H256::new(); + { + let mut t = TrieDBMut::new(&mut memdb, &mut root); + t.insert(b"A", b"ABC").unwrap(); + t.insert(b"B", b"ABCBA").unwrap(); + } + + let t = TrieDB::new(&memdb, &root).unwrap(); + + // query for an invalid data type to trigger an error + let q = rlp::decode::; + let lookup = Lookup{ db: t.db, query: q, hash: root }; + let query_result = lookup.look_up(NibbleSlice::new(b"A")); + assert_eq!(query_result.unwrap().unwrap().unwrap_err(), rlp::DecoderError::RlpIsTooShort); +} diff --git a/util/patricia_trie/src/triedbmut.rs b/util/patricia_trie/src/triedbmut.rs index 98215de012fd5bebe81d6ef1461ac8138bf1d39c..994045eb3a7ac60464fbbaf6ae8993704e389371 100644 --- a/util/patricia_trie/src/triedbmut.rs +++ b/util/patricia_trie/src/triedbmut.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -24,7 +24,7 @@ use super::node::NodeKey; use hashdb::HashDB; use bytes::ToPretty; use nibbleslice::NibbleSlice; -use rlp::{UntrustedRlp, RlpStream}; +use rlp::{Rlp, RlpStream}; use hashdb::DBValue; use std::collections::{HashSet, VecDeque}; @@ -107,7 +107,7 @@ impl Node { RlpNode::Branch(ref children_rlp, val) => { let mut child = |i| { let raw = children_rlp[i]; - let child_rlp = UntrustedRlp::new(raw); + let child_rlp = Rlp::new(raw); if !child_rlp.is_empty() { Some(Self::inline_or_hash(raw, db, storage)) } else { @@ -893,7 +893,6 @@ impl<'a> TrieMut for TrieDBMut<'a> { self.lookup(NibbleSlice::new(key), &self.root_handle) } - fn insert(&mut self, key: &[u8], value: &[u8]) -> super::Result> { if value.is_empty() { return self.remove(key) } diff --git a/util/plain_hasher/Cargo.toml b/util/plain_hasher/Cargo.toml index d79ea4c5e42c58d93451f72a7dcff1e6e848a5be..d909f8f9d6ba3b007c1dd96c01c32a305d64a7d0 100644 --- a/util/plain_hasher/Cargo.toml +++ b/util/plain_hasher/Cargo.toml @@ -9,4 +9,4 @@ homepage = "https://github.com/paritytech/plain_hasher" [dependencies] crunchy = "0.1.6" -ethereum-types = "0.2" +ethereum-types = "0.3" diff --git a/util/plain_hasher/benches/bench.rs b/util/plain_hasher/benches/bench.rs index e7e8570abb2104a88c88d2b5061e9b3f54dedcc7..cfaa95eaa6dcf340b3903a3d650e4262ed012f24 100644 --- a/util/plain_hasher/benches/bench.rs +++ b/util/plain_hasher/benches/bench.rs @@ -1,3 +1,19 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + #![feature(test)] extern crate test; diff --git a/util/plain_hasher/src/lib.rs b/util/plain_hasher/src/lib.rs index 54bad92f47ede21336a912732f9ee2e113c38bbb..74e2225dc8ff54fc70c645e780a429172e087bf3 100644 --- a/util/plain_hasher/src/lib.rs +++ b/util/plain_hasher/src/lib.rs @@ -1,10 +1,26 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + #[macro_use] extern crate crunchy; extern crate ethereum_types; -use std::{hash, mem}; -use std::collections::{HashMap, HashSet}; use ethereum_types::H256; +use std::collections::{HashMap, HashSet}; +use std::hash; /// Specialized version of `HashMap` with H256 keys and fast hashing function. pub type H256FastMap = HashMap>; @@ -28,16 +44,13 @@ impl hash::Hasher for PlainHasher { #[allow(unused_assignments)] fn write(&mut self, bytes: &[u8]) { debug_assert!(bytes.len() == 32); + let mut bytes_ptr = bytes.as_ptr(); + let mut prefix_ptr = &mut self.prefix as *mut u64 as *mut u8; - unsafe { - let mut bytes_ptr = bytes.as_ptr(); - let prefix_u8: &mut [u8; 8] = mem::transmute(&mut self.prefix); - let mut prefix_ptr = prefix_u8.as_mut_ptr(); - - unroll! { - for _i in 0..8 { + unroll! { + for _i in 0..8 { + unsafe { *prefix_ptr ^= (*bytes_ptr ^ *bytes_ptr.offset(8)) ^ (*bytes_ptr.offset(16) ^ *bytes_ptr.offset(24)); - bytes_ptr = bytes_ptr.offset(1); prefix_ptr = prefix_ptr.offset(1); } diff --git a/util/reactor/src/lib.rs b/util/reactor/src/lib.rs index f5a37fb0fcb35a40bdb058a81900b62666ac872e..8fd37e7c8861d2afeffdc1009fc9e2fad6cff5b6 100644 --- a/util/reactor/src/lib.rs +++ b/util/reactor/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -14,7 +14,6 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . - //! Tokio Core Reactor wrapper. extern crate futures; @@ -24,7 +23,7 @@ use std::{fmt, thread}; use std::sync::mpsc; use std::time::Duration; use futures::{Future, IntoFuture}; -pub use tokio_core::reactor::{Remote as TokioRemote, Timeout}; +pub use tokio_core::reactor::{Remote as TokioRemote, Handle, Timeout}; /// Event Loop for futures. /// Wrapper around `tokio::reactor::Core`. @@ -122,7 +121,6 @@ impl Remote { } } - /// Spawn a future to this event loop pub fn spawn(&self, r: R) where R: IntoFuture + Send + 'static, @@ -143,18 +141,22 @@ impl Remote { /// Spawn a new future returned by given closure. pub fn spawn_fn(&self, f: F) where - F: FnOnce() -> R + Send + 'static, + F: FnOnce(&Handle) -> R + Send + 'static, R: IntoFuture, R::Future: 'static, { match self.inner { - Mode::Tokio(ref remote) => remote.spawn(move |_| f()), + Mode::Tokio(ref remote) => remote.spawn(move |handle| f(handle)), Mode::Sync => { - let _ = f().into_future().wait(); + let mut core = tokio_core::reactor::Core::new().expect("Creating an event loop should not fail."); + let handle = core.handle(); + let _ = core.run(f(&handle).into_future()); }, Mode::ThreadPerFuture => { thread::spawn(move || { - let _= f().into_future().wait(); + let mut core = tokio_core::reactor::Core::new().expect("Creating an event loop should not fail."); + let handle = core.handle(); + let _ = core.run(f(&handle).into_future()); }); }, } @@ -163,13 +165,13 @@ impl Remote { /// Spawn a new future and wait for it or for a timeout to occur. pub fn spawn_with_timeout(&self, f: F, duration: Duration, on_timeout: T) where T: FnOnce() -> () + Send + 'static, - F: FnOnce() -> R + Send + 'static, + F: FnOnce(&Handle) -> R + Send + 'static, R: IntoFuture, R::Future: 'static, { match self.inner { Mode::Tokio(ref remote) => remote.spawn(move |handle| { - let future = f().into_future(); + let future = f(handle).into_future(); let timeout = Timeout::new(duration, handle).expect("Event loop is still up."); future.select(timeout.then(move |_| { on_timeout(); @@ -177,11 +179,25 @@ impl Remote { })).then(|_| Ok(())) }), Mode::Sync => { - let _ = f().into_future().wait(); + let mut core = tokio_core::reactor::Core::new().expect("Creating an event loop should not fail."); + let handle = core.handle(); + let future = f(&handle).into_future(); + let timeout = Timeout::new(duration, &handle).expect("Event loop is still up."); + let _: Result<(), ()> = core.run(future.select(timeout.then(move |_| { + on_timeout(); + Ok(()) + })).then(|_| Ok(()))); }, Mode::ThreadPerFuture => { thread::spawn(move || { - let _ = f().into_future().wait(); + let mut core = tokio_core::reactor::Core::new().expect("Creating an event loop should not fail."); + let handle = core.handle(); + let future = f(&handle).into_future(); + let timeout = Timeout::new(duration, &handle).expect("Event loop is still up."); + let _: Result<(), ()> = core.run(future.select(timeout.then(move |_| { + on_timeout(); + Ok(()) + })).then(|_| Ok(()))); }); }, } diff --git a/util/rlp/Cargo.toml b/util/rlp/Cargo.toml index ecb3b39312b4f34899c574abbb8497d4935265bc..3bde7206ff8b414c04d30b0488422ca04ceac517 100644 --- a/util/rlp/Cargo.toml +++ b/util/rlp/Cargo.toml @@ -8,6 +8,6 @@ authors = ["Parity Technologies "] [dependencies] elastic-array = "0.10" -ethereum-types = "0.2" +ethereum-types = "0.3" rustc-hex = "1.0" byteorder = "1.0" diff --git a/util/rlp/src/error.rs b/util/rlp/src/error.rs index 5113fdc17dfdca85d50e1657eeea06fe88f1d444..7aef6cfbf72ea1844e5a595a1368ffccd9c0c2d4 100644 --- a/util/rlp/src/error.rs +++ b/util/rlp/src/error.rs @@ -9,7 +9,7 @@ use std::fmt; use std::error::Error as StdError; -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Clone)] /// Error concerning the RLP decoder. pub enum DecoderError { /// Data has additional bytes at the end of the valid RLP fragment. diff --git a/util/rlp/src/impls.rs b/util/rlp/src/impls.rs index 7cb6572fec9123deb6d9c4ae1de4afdad7dd308b..573f2c0781ec0fe97ba77cf4c96cd6c045a8dd45 100644 --- a/util/rlp/src/impls.rs +++ b/util/rlp/src/impls.rs @@ -11,7 +11,7 @@ use byteorder::{ByteOrder, BigEndian}; use bigint::{U128, U256, H64, H128, H160, H256, H512, H520, Bloom}; use traits::{Encodable, Decodable}; use stream::RlpStream; -use {UntrustedRlp, DecoderError}; +use {Rlp, DecoderError}; pub fn decode_usize(bytes: &[u8]) -> Result { match bytes.len() { @@ -41,7 +41,7 @@ impl Encodable for bool { } impl Decodable for bool { - fn decode(rlp: &UntrustedRlp) -> Result { + fn decode(rlp: &Rlp) -> Result { rlp.decoder().decode_value(|bytes| { match bytes.len() { 0 => Ok(false), @@ -65,7 +65,7 @@ impl Encodable for Vec { } impl Decodable for Vec { - fn decode(rlp: &UntrustedRlp) -> Result { + fn decode(rlp: &Rlp) -> Result { rlp.decoder().decode_value(|bytes| { Ok(bytes.to_vec()) }) @@ -87,7 +87,7 @@ impl Encodable for Option where T: Encodable { } impl Decodable for Option where T: Decodable { - fn decode(rlp: &UntrustedRlp) -> Result { + fn decode(rlp: &Rlp) -> Result { let items = rlp.item_count()?; match items { 1 => rlp.val_at(0).map(Some), @@ -108,7 +108,7 @@ impl Encodable for u8 { } impl Decodable for u8 { - fn decode(rlp: &UntrustedRlp) -> Result { + fn decode(rlp: &Rlp) -> Result { rlp.decoder().decode_value(|bytes| { match bytes.len() { 1 if bytes[0] != 0 => Ok(bytes[0]), @@ -136,7 +136,7 @@ macro_rules! impl_encodable_for_u { macro_rules! impl_decodable_for_u { ($name: ident) => { impl Decodable for $name { - fn decode(rlp: &UntrustedRlp) -> Result { + fn decode(rlp: &Rlp) -> Result { rlp.decoder().decode_value(|bytes| { match bytes.len() { 0 | 1 => u8::decode(rlp).map(|v| v as $name), @@ -174,7 +174,7 @@ impl Encodable for usize { } impl Decodable for usize { - fn decode(rlp: &UntrustedRlp) -> Result { + fn decode(rlp: &Rlp) -> Result { u64::decode(rlp).map(|value| value as usize) } } @@ -192,7 +192,7 @@ macro_rules! impl_encodable_for_hash { macro_rules! impl_decodable_for_hash { ($name: ident, $size: expr) => { impl Decodable for $name { - fn decode(rlp: &UntrustedRlp) -> Result { + fn decode(rlp: &Rlp) -> Result { rlp.decoder().decode_value(|bytes| match bytes.len().cmp(&$size) { cmp::Ordering::Less => Err(DecoderError::RlpIsTooShort), cmp::Ordering::Greater => Err(DecoderError::RlpIsTooBig), @@ -239,7 +239,7 @@ macro_rules! impl_encodable_for_uint { macro_rules! impl_decodable_for_uint { ($name: ident, $size: expr) => { impl Decodable for $name { - fn decode(rlp: &UntrustedRlp) -> Result { + fn decode(rlp: &Rlp) -> Result { rlp.decoder().decode_value(|bytes| { if !bytes.is_empty() && bytes[0] == 0 { Err(DecoderError::RlpInvalidIndirection) @@ -273,7 +273,7 @@ impl Encodable for String { } impl Decodable for String { - fn decode(rlp: &UntrustedRlp) -> Result { + fn decode(rlp: &Rlp) -> Result { rlp.decoder().decode_value(|bytes| { match str::from_utf8(bytes) { Ok(s) => Ok(s.to_owned()), diff --git a/util/rlp/src/lib.rs b/util/rlp/src/lib.rs index a5889a4efc22efb84cd178e7f18c7eacc4c370b7..b416b1c25b0090deebddc6c127c19c88838b3d54 100644 --- a/util/rlp/src/lib.rs +++ b/util/rlp/src/lib.rs @@ -27,12 +27,6 @@ //! * You encode a big set of data. //! //!### Use `Rlp` when: -//! * You are working on trusted data (not corrupted). -//! * You want to get view onto rlp-slice. -//! * You don't want to decode whole rlp at once. -//! -//!### Use `UntrustedRlp` when: -//! * You are working on untrusted data (~corrupted). //! * You need to handle data corruption errors. //! * You are working on input data. //! * You want to get view onto rlp-slice. @@ -46,7 +40,6 @@ extern crate rustc_hex; mod traits; mod error; mod rlpin; -mod untrusted_rlp; mod stream; mod impls; @@ -55,8 +48,7 @@ use elastic_array::ElasticArray1024; pub use error::DecoderError; pub use traits::{Decodable, Encodable}; -pub use untrusted_rlp::{UntrustedRlp, UntrustedRlpIterator, PayloadInfo, Prototype}; -pub use rlpin::{Rlp, RlpIterator}; +pub use rlpin::{Rlp, RlpIterator, PayloadInfo, Prototype}; pub use stream::RlpStream; /// The RLP encoded empty data (used to mean "null value"). @@ -71,18 +63,18 @@ pub const EMPTY_LIST_RLP: [u8; 1] = [0xC0; 1]; /// /// fn main () { /// let data = vec![0x83, b'c', b'a', b't']; -/// let animal: String = rlp::decode(&data); +/// let animal: String = rlp::decode(&data).expect("could not decode"); /// assert_eq!(animal, "cat".to_owned()); /// } /// ``` -pub fn decode(bytes: &[u8]) -> T where T: Decodable { +pub fn decode(bytes: &[u8]) -> Result where T: Decodable { let rlp = Rlp::new(bytes); rlp.as_val() } pub fn decode_list(bytes: &[u8]) -> Vec where T: Decodable { let rlp = Rlp::new(bytes); - rlp.as_list() + rlp.as_list().expect("trusted rlp should be valid") } /// Shortcut function to encode structure into rlp. diff --git a/util/rlp/src/rlpin.rs b/util/rlp/src/rlpin.rs index c9679591308aeeabc228e6f5dea307549df9a581..23fdc452e7856b16952d16bcb236e8b2f0417ef7 100644 --- a/util/rlp/src/rlpin.rs +++ b/util/rlp/src/rlpin.rs @@ -6,246 +6,294 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::cell::Cell; use std::fmt; -use {UntrustedRlp, PayloadInfo, Prototype, Decodable}; +use rustc_hex::ToHex; +use impls::decode_usize; +use {Decodable, DecoderError}; -impl<'a> From> for Rlp<'a> { - fn from(rlp: UntrustedRlp<'a>) -> Rlp<'a> { - Rlp { rlp: rlp } +/// rlp offset +#[derive(Copy, Clone, Debug)] +struct OffsetCache { + index: usize, + offset: usize, +} + +impl OffsetCache { + fn new(index: usize, offset: usize) -> OffsetCache { + OffsetCache { + index: index, + offset: offset, + } + } +} + +#[derive(Debug)] +/// RLP prototype +pub enum Prototype { + /// Empty + Null, + /// Value + Data(usize), + /// List + List(usize), +} + +/// Stores basic information about item +pub struct PayloadInfo { + /// Header length in bytes + pub header_len: usize, + /// Value length in bytes + pub value_len: usize, +} + +fn calculate_payload_info(header_bytes: &[u8], len_of_len: usize) -> Result { + let header_len = 1 + len_of_len; + match header_bytes.get(1) { + Some(&0) => return Err(DecoderError::RlpDataLenWithZeroPrefix), + None => return Err(DecoderError::RlpIsTooShort), + _ => (), } + if header_bytes.len() < header_len { return Err(DecoderError::RlpIsTooShort); } + let value_len = decode_usize(&header_bytes[1..header_len])?; + Ok(PayloadInfo::new(header_len, value_len)) } -/// Data-oriented view onto trusted rlp-slice. +impl PayloadInfo { + fn new(header_len: usize, value_len: usize) -> PayloadInfo { + PayloadInfo { + header_len: header_len, + value_len: value_len, + } + } + + /// Total size of the RLP. + pub fn total(&self) -> usize { self.header_len + self.value_len } + + /// Create a new object from the given bytes RLP. The bytes + pub fn from(header_bytes: &[u8]) -> Result { + match header_bytes.first().cloned() { + None => Err(DecoderError::RlpIsTooShort), + Some(0...0x7f) => Ok(PayloadInfo::new(0, 1)), + Some(l @ 0x80...0xb7) => Ok(PayloadInfo::new(1, l as usize - 0x80)), + Some(l @ 0xb8...0xbf) => { + let len_of_len = l as usize - 0xb7; + calculate_payload_info(header_bytes, len_of_len) + } + Some(l @ 0xc0...0xf7) => Ok(PayloadInfo::new(1, l as usize - 0xc0)), + Some(l @ 0xf8...0xff) => { + let len_of_len = l as usize - 0xf7; + calculate_payload_info(header_bytes, len_of_len) + }, + // we cant reach this place, but rust requires _ to be implemented + _ => { unreachable!(); } + } + } +} + +/// Data-oriented view onto rlp-slice. +/// +/// This is an immutable structure. No operations change it. /// -/// Unlikely to `UntrustedRlp` doesn't bother you with error -/// handling. It assumes that you know what you are doing. +/// Should be used in places where, error handling is required, +/// eg. on input #[derive(Debug)] pub struct Rlp<'a> { - rlp: UntrustedRlp<'a> + bytes: &'a [u8], + offset_cache: Cell, + count_cache: Cell>, +} + +impl<'a> Clone for Rlp<'a> { + fn clone(&self) -> Rlp<'a> { + Rlp { + bytes: self.bytes, + offset_cache: self.offset_cache.clone(), + count_cache: self.count_cache.clone(), + } + } } impl<'a> fmt::Display for Rlp<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!(f, "{}", self.rlp) + match self.prototype() { + Ok(Prototype::Null) => write!(f, "null"), + Ok(Prototype::Data(_)) => write!(f, "\"0x{}\"", self.data().unwrap().to_hex()), + Ok(Prototype::List(len)) => { + write!(f, "[")?; + for i in 0..len-1 { + write!(f, "{}, ", self.at(i).unwrap())?; + } + write!(f, "{}", self.at(len - 1).unwrap())?; + write!(f, "]") + }, + Err(err) => write!(f, "{:?}", err) + } } } impl<'a, 'view> Rlp<'a> where 'a: 'view { - /// Create a new instance of `Rlp` pub fn new(bytes: &'a [u8]) -> Rlp<'a> { Rlp { - rlp: UntrustedRlp::new(bytes) + bytes: bytes, + offset_cache: Cell::new(OffsetCache::new(usize::max_value(), 0)), + count_cache: Cell::new(None) } } - /// The raw data of the RLP as slice. - /// - /// ```rust - /// extern crate rlp; - /// use rlp::*; - /// - /// fn main () { - /// let data = vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g']; - /// let rlp = Rlp::new(&data); - /// let dog = rlp.at(1).as_raw(); - /// assert_eq!(dog, &[0x83, b'd', b'o', b'g']); - /// } - /// ``` pub fn as_raw(&'view self) -> &'a [u8] { - self.rlp.as_raw() - } - - /// Get the prototype of the RLP. - pub fn prototype(&self) -> Prototype { - self.rlp.prototype().unwrap() - } - - /// Get payload info. - pub fn payload_info(&self) -> PayloadInfo { - self.rlp.payload_info().unwrap() - } - - /// Get underlieing data. - pub fn data(&'view self) -> &'a [u8] { - self.rlp.data().unwrap() - } - - /// Returns number of RLP items. - /// - /// ```rust - /// extern crate rlp; - /// use rlp::*; - /// - /// fn main () { - /// let data = vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g']; - /// let rlp = Rlp::new(&data); - /// assert_eq!(rlp.item_count(), 2); - /// let view = rlp.at(1); - /// assert_eq!(view.item_count(), 0); - /// } - /// ``` - pub fn item_count(&self) -> usize { - self.rlp.item_count().unwrap_or(0) - } - - /// Returns the number of bytes in the data, or zero if it isn't data. - /// - /// ```rust - /// extern crate rlp; - /// use rlp::*; - /// - /// fn main () { - /// let data = vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g']; - /// let rlp = Rlp::new(&data); - /// assert_eq!(rlp.size(), 0); - /// let view = rlp.at(1); - /// assert_eq!(view.size(), 3); - /// } - /// ``` + self.bytes + } + + pub fn prototype(&self) -> Result { + // optimize? && return appropriate errors + if self.is_data() { + Ok(Prototype::Data(self.size())) + } else if self.is_list() { + self.item_count().map(Prototype::List) + } else { + Ok(Prototype::Null) + } + } + + pub fn payload_info(&self) -> Result { + BasicDecoder::payload_info(self.bytes) + } + + pub fn data(&'view self) -> Result<&'a [u8], DecoderError> { + let pi = BasicDecoder::payload_info(self.bytes)?; + Ok(&self.bytes[pi.header_len..(pi.header_len + pi.value_len)]) + } + + pub fn item_count(&self) -> Result { + match self.is_list() { + true => match self.count_cache.get() { + Some(c) => Ok(c), + None => { + let c = self.iter().count(); + self.count_cache.set(Some(c)); + Ok(c) + } + }, + false => Err(DecoderError::RlpExpectedToBeList), + } + } + pub fn size(&self) -> usize { - self.rlp.size() - } - - /// Get view onto RLP-slice at index. - /// - /// Caches offset to given index, so access to successive - /// slices is faster. - /// - /// ```rust - /// extern crate rlp; - /// use rlp::*; - /// - /// fn main () { - /// let data = vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g']; - /// let rlp = Rlp::new(&data); - /// let dog: String = rlp.at(1).as_val(); - /// assert_eq!(dog, "dog".to_string()); - /// } - /// ``` - pub fn at(&'view self, index: usize) -> Rlp<'a> { - From::from(self.rlp.at(index).unwrap()) - } - - /// No value - /// - /// ```rust - /// extern crate rlp; - /// use rlp::*; - /// - /// fn main () { - /// let data = vec![]; - /// let rlp = Rlp::new(&data); - /// assert!(rlp.is_null()); - /// } - /// ``` + match self.is_data() { + // TODO: No panic on malformed data, but ideally would Err on no PayloadInfo. + true => BasicDecoder::payload_info(self.bytes).map(|b| b.value_len).unwrap_or(0), + false => 0 + } + } + + pub fn at(&'view self, index: usize) -> Result, DecoderError> { + if !self.is_list() { + return Err(DecoderError::RlpExpectedToBeList); + } + + // move to cached position if its index is less or equal to + // current search index, otherwise move to beginning of list + let c = self.offset_cache.get(); + let (mut bytes, to_skip) = match c.index <= index { + true => (Rlp::consume(self.bytes, c.offset)?, index - c.index), + false => (self.consume_list_payload()?, index), + }; + + // skip up to x items + bytes = Rlp::consume_items(bytes, to_skip)?; + + // update the cache + self.offset_cache.set(OffsetCache::new(index, self.bytes.len() - bytes.len())); + + // construct new rlp + let found = BasicDecoder::payload_info(bytes)?; + Ok(Rlp::new(&bytes[0..found.header_len + found.value_len])) + } + pub fn is_null(&self) -> bool { - self.rlp.is_null() - } - - /// Contains a zero-length string or zero-length list. - /// - /// ```rust - /// extern crate rlp; - /// use rlp::*; - /// - /// fn main () { - /// let data = vec![0xc0]; - /// let rlp = Rlp::new(&data); - /// assert!(rlp.is_empty()); - /// } - /// ``` + self.bytes.len() == 0 + } + pub fn is_empty(&self) -> bool { - self.rlp.is_empty() - } - - /// List value - /// - /// ```rust - /// extern crate rlp; - /// use rlp::*; - /// - /// fn main () { - /// let data = vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g']; - /// let rlp = Rlp::new(&data); - /// assert!(rlp.is_list()); - /// } - /// ``` + !self.is_null() && (self.bytes[0] == 0xc0 || self.bytes[0] == 0x80) + } + pub fn is_list(&self) -> bool { - self.rlp.is_list() - } - - /// String value - /// - /// ```rust - /// extern crate rlp; - /// use rlp::*; - /// - /// fn main () { - /// let data = vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g']; - /// let rlp = Rlp::new(&data); - /// assert!(rlp.at(1).is_data()); - /// } - /// ``` + !self.is_null() && self.bytes[0] >= 0xc0 + } + pub fn is_data(&self) -> bool { - self.rlp.is_data() - } - - /// Int value - /// - /// ```rust - /// extern crate rlp; - /// use rlp::*; - /// - /// fn main () { - /// let data = vec![0xc1, 0x10]; - /// let rlp = Rlp::new(&data); - /// assert_eq!(rlp.is_int(), false); - /// assert_eq!(rlp.at(0).is_int(), true); - /// } - /// ``` + !self.is_null() && self.bytes[0] < 0xc0 + } + pub fn is_int(&self) -> bool { - self.rlp.is_int() - } - - /// Get iterator over rlp-slices - /// - /// ```rust - /// extern crate rlp; - /// use rlp::*; - /// - /// fn main () { - /// let data = vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g']; - /// let rlp = Rlp::new(&data); - /// let strings: Vec = rlp.iter().map(| i | i.as_val()).collect(); - /// } - /// ``` + if self.is_null() { + return false; + } + + match self.bytes[0] { + 0...0x80 => true, + 0x81...0xb7 => self.bytes[1] != 0, + b @ 0xb8...0xbf => self.bytes[1 + b as usize - 0xb7] != 0, + _ => false + } + } + pub fn iter(&'view self) -> RlpIterator<'a, 'view> { self.into_iter() } - /// Decode data into an object - pub fn as_val(&self) -> T where T: Decodable { - self.rlp.as_val().expect("Unexpected rlp error") + pub fn as_val(&self) -> Result where T: Decodable { + T::decode(self) } - pub fn as_list(&self) -> Vec where T: Decodable { + pub fn as_list(&self) -> Result, DecoderError> where T: Decodable { self.iter().map(|rlp| rlp.as_val()).collect() } - /// Decode data at given list index into an object - pub fn val_at(&self, index: usize) -> T where T: Decodable { - self.at(index).as_val() + pub fn val_at(&self, index: usize) -> Result where T: Decodable { + self.at(index)?.as_val() + } + + pub fn list_at(&self, index: usize) -> Result, DecoderError> where T: Decodable { + self.at(index)?.as_list() + } + + pub fn decoder(&self) -> BasicDecoder { + BasicDecoder::new(self.clone()) + } + + /// consumes first found prefix + fn consume_list_payload(&self) -> Result<&'a [u8], DecoderError> { + let item = BasicDecoder::payload_info(self.bytes)?; + let bytes = Rlp::consume(self.bytes, item.header_len)?; + Ok(bytes) } - pub fn list_at(&self, index: usize) -> Vec where T: Decodable { - self.at(index).as_list() + /// consumes fixed number of items + fn consume_items(bytes: &'a [u8], items: usize) -> Result<&'a [u8], DecoderError> { + let mut result = bytes; + for _ in 0..items { + let i = BasicDecoder::payload_info(result)?; + result = Rlp::consume(result, i.header_len + i.value_len)?; + } + Ok(result) + } + + /// consumes slice prefix of length `len` + fn consume(bytes: &'a [u8], len: usize) -> Result<&'a [u8], DecoderError> { + match bytes.len() >= len { + true => Ok(&bytes[len..]), + false => Err(DecoderError::RlpIsTooShort), + } } } -/// Iterator over trusted rlp-slice list elements. +/// Iterator over rlp-slice list elements. pub struct RlpIterator<'a, 'view> where 'a: 'view { rlp: &'view Rlp<'a>, - index: usize + index: usize, } impl<'a, 'view> IntoIterator for &'view Rlp<'a> where 'a: 'view { @@ -265,19 +313,93 @@ impl<'a, 'view> Iterator for RlpIterator<'a, 'view> { fn next(&mut self) -> Option> { let index = self.index; - let result = self.rlp.rlp.at(index).ok().map(From::from); + let result = self.rlp.at(index).ok(); self.index += 1; result } } -#[test] -fn break_it() { - use rustc_hex::FromHex; - use bigint::U256; +pub struct BasicDecoder<'a> { + rlp: Rlp<'a> +} + +impl<'a> BasicDecoder<'a> { + pub fn new(rlp: Rlp<'a>) -> BasicDecoder<'a> { + BasicDecoder { + rlp: rlp + } + } + + /// Return first item info. + fn payload_info(bytes: &[u8]) -> Result { + let item = PayloadInfo::from(bytes)?; + match item.header_len.checked_add(item.value_len) { + Some(x) if x <= bytes.len() => Ok(item), + _ => Err(DecoderError::RlpIsTooShort), + } + } + + pub fn decode_value(&self, f: F) -> Result + where F: Fn(&[u8]) -> Result { + + let bytes = self.rlp.as_raw(); + + match bytes.first().cloned() { + // RLP is too short. + None => Err(DecoderError::RlpIsTooShort), + // Single byte value. + Some(l @ 0...0x7f) => Ok(f(&[l])?), + // 0-55 bytes + Some(l @ 0x80...0xb7) => { + let last_index_of = 1 + l as usize - 0x80; + if bytes.len() < last_index_of { + return Err(DecoderError::RlpInconsistentLengthAndData); + } + let d = &bytes[1..last_index_of]; + if l == 0x81 && d[0] < 0x80 { + return Err(DecoderError::RlpInvalidIndirection); + } + Ok(f(d)?) + }, + // Longer than 55 bytes. + Some(l @ 0xb8...0xbf) => { + let len_of_len = l as usize - 0xb7; + let begin_of_value = 1 as usize + len_of_len; + if bytes.len() < begin_of_value { + return Err(DecoderError::RlpInconsistentLengthAndData); + } + let len = decode_usize(&bytes[1..begin_of_value])?; + + let last_index_of_value = begin_of_value.checked_add(len) + .ok_or(DecoderError::RlpInvalidLength)?; + if bytes.len() < last_index_of_value { + return Err(DecoderError::RlpInconsistentLengthAndData); + } + Ok(f(&bytes[begin_of_value..last_index_of_value])?) + } + // We are reading value, not a list! + _ => Err(DecoderError::RlpExpectedToBeData) + } + } +} + +#[cfg(test)] +mod tests { + use {Rlp, DecoderError}; - let h: Vec = FromHex::from_hex("f84d0589010efbef67941f79b2a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470").unwrap(); - let r: Rlp = Rlp::new(&h); - let u: U256 = r.val_at(1); - assert_eq!(format!("{}", u), "19526463837540678066"); + #[test] + fn test_rlp_display() { + use rustc_hex::FromHex; + let data = "f84d0589010efbef67941f79b2a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".from_hex().unwrap(); + let rlp = Rlp::new(&data); + assert_eq!(format!("{}", rlp), "[\"0x05\", \"0x010efbef67941f79b2\", \"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\", \"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470\"]"); + } + + #[test] + fn length_overflow() { + let bs = [0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe5]; + let rlp = Rlp::new(&bs); + let res: Result = rlp.as_val(); + assert_eq!(Err(DecoderError::RlpInvalidLength), res); + } } diff --git a/util/rlp/src/stream.rs b/util/rlp/src/stream.rs index 000b6e15bcbe8714283ed2139528b510929f058f..550ede039917d550b0384ba68d60776a5891781f 100644 --- a/util/rlp/src/stream.rs +++ b/util/rlp/src/stream.rs @@ -133,7 +133,6 @@ impl RlpStream { self } - /// Declare appending the list of unknown size, chainable. pub fn begin_unbounded_list(&mut self) -> &mut RlpStream { self.finished_list = false; @@ -206,7 +205,6 @@ impl RlpStream { base_size } - /// Returns current RLP size in bytes for the data pushed into the list. pub fn len<'a>(&'a self) -> usize { self.estimate_size(0) diff --git a/util/rlp/src/traits.rs b/util/rlp/src/traits.rs index 193d0bf84adfb6727770494a9631a19729e5df8f..1596009e75f40c3446d47ac5e7fee2d2e9df997e 100644 --- a/util/rlp/src/traits.rs +++ b/util/rlp/src/traits.rs @@ -8,12 +8,12 @@ //! Common RLP traits use elastic_array::ElasticArray1024; -use {DecoderError, UntrustedRlp, RlpStream}; +use {DecoderError, Rlp, RlpStream}; /// RLP decodable trait pub trait Decodable: Sized { /// Decode a value from RLP bytes - fn decode(rlp: &UntrustedRlp) -> Result; + fn decode(rlp: &Rlp) -> Result; } /// Structure encodable to RLP diff --git a/util/rlp/src/untrusted_rlp.rs b/util/rlp/src/untrusted_rlp.rs deleted file mode 100644 index 7f95ce2efedaa80e00f75851ebf762a8c2c48443..0000000000000000000000000000000000000000 --- a/util/rlp/src/untrusted_rlp.rs +++ /dev/null @@ -1,406 +0,0 @@ -// Copyright 2015-2017 Parity Technologies -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use std::cell::Cell; -use std::fmt; -use rustc_hex::ToHex; -use impls::decode_usize; -use {Decodable, DecoderError}; - -/// rlp offset -#[derive(Copy, Clone, Debug)] -struct OffsetCache { - index: usize, - offset: usize, -} - -impl OffsetCache { - fn new(index: usize, offset: usize) -> OffsetCache { - OffsetCache { - index: index, - offset: offset, - } - } -} - -#[derive(Debug)] -/// RLP prototype -pub enum Prototype { - /// Empty - Null, - /// Value - Data(usize), - /// List - List(usize), -} - -/// Stores basic information about item -pub struct PayloadInfo { - /// Header length in bytes - pub header_len: usize, - /// Value length in bytes - pub value_len: usize, -} - -fn calculate_payload_info(header_bytes: &[u8], len_of_len: usize) -> Result { - let header_len = 1 + len_of_len; - match header_bytes.get(1) { - Some(&0) => return Err(DecoderError::RlpDataLenWithZeroPrefix), - None => return Err(DecoderError::RlpIsTooShort), - _ => (), - } - if header_bytes.len() < header_len { return Err(DecoderError::RlpIsTooShort); } - let value_len = decode_usize(&header_bytes[1..header_len])?; - Ok(PayloadInfo::new(header_len, value_len)) -} - -impl PayloadInfo { - fn new(header_len: usize, value_len: usize) -> PayloadInfo { - PayloadInfo { - header_len: header_len, - value_len: value_len, - } - } - - /// Total size of the RLP. - pub fn total(&self) -> usize { self.header_len + self.value_len } - - /// Create a new object from the given bytes RLP. The bytes - pub fn from(header_bytes: &[u8]) -> Result { - match header_bytes.first().cloned() { - None => Err(DecoderError::RlpIsTooShort), - Some(0...0x7f) => Ok(PayloadInfo::new(0, 1)), - Some(l @ 0x80...0xb7) => Ok(PayloadInfo::new(1, l as usize - 0x80)), - Some(l @ 0xb8...0xbf) => { - let len_of_len = l as usize - 0xb7; - calculate_payload_info(header_bytes, len_of_len) - } - Some(l @ 0xc0...0xf7) => Ok(PayloadInfo::new(1, l as usize - 0xc0)), - Some(l @ 0xf8...0xff) => { - let len_of_len = l as usize - 0xf7; - calculate_payload_info(header_bytes, len_of_len) - }, - // we cant reach this place, but rust requires _ to be implemented - _ => { unreachable!(); } - } - } -} - -/// Data-oriented view onto rlp-slice. -/// -/// This is an immutable structure. No operations change it. -/// -/// Should be used in places where, error handling is required, -/// eg. on input -#[derive(Debug)] -pub struct UntrustedRlp<'a> { - bytes: &'a [u8], - offset_cache: Cell, - count_cache: Cell>, -} - -impl<'a> Clone for UntrustedRlp<'a> { - fn clone(&self) -> UntrustedRlp<'a> { - UntrustedRlp { - bytes: self.bytes, - offset_cache: self.offset_cache.clone(), - count_cache: self.count_cache.clone(), - } - } -} - -impl<'a> fmt::Display for UntrustedRlp<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - match self.prototype() { - Ok(Prototype::Null) => write!(f, "null"), - Ok(Prototype::Data(_)) => write!(f, "\"0x{}\"", self.data().unwrap().to_hex()), - Ok(Prototype::List(len)) => { - write!(f, "[")?; - for i in 0..len-1 { - write!(f, "{}, ", self.at(i).unwrap())?; - } - write!(f, "{}", self.at(len - 1).unwrap())?; - write!(f, "]") - }, - Err(err) => write!(f, "{:?}", err) - } - } -} - -impl<'a, 'view> UntrustedRlp<'a> where 'a: 'view { - pub fn new(bytes: &'a [u8]) -> UntrustedRlp<'a> { - UntrustedRlp { - bytes: bytes, - offset_cache: Cell::new(OffsetCache::new(usize::max_value(), 0)), - count_cache: Cell::new(None) - } - } - - pub fn as_raw(&'view self) -> &'a [u8] { - self.bytes - } - - pub fn prototype(&self) -> Result { - // optimize? && return appropriate errors - if self.is_data() { - Ok(Prototype::Data(self.size())) - } else if self.is_list() { - self.item_count().map(Prototype::List) - } else { - Ok(Prototype::Null) - } - } - - pub fn payload_info(&self) -> Result { - BasicDecoder::payload_info(self.bytes) - } - - pub fn data(&'view self) -> Result<&'a [u8], DecoderError> { - let pi = BasicDecoder::payload_info(self.bytes)?; - Ok(&self.bytes[pi.header_len..(pi.header_len + pi.value_len)]) - } - - pub fn item_count(&self) -> Result { - match self.is_list() { - true => match self.count_cache.get() { - Some(c) => Ok(c), - None => { - let c = self.iter().count(); - self.count_cache.set(Some(c)); - Ok(c) - } - }, - false => Err(DecoderError::RlpExpectedToBeList), - } - } - - pub fn size(&self) -> usize { - match self.is_data() { - // TODO: No panic on malformed data, but ideally would Err on no PayloadInfo. - true => BasicDecoder::payload_info(self.bytes).map(|b| b.value_len).unwrap_or(0), - false => 0 - } - } - - pub fn at(&'view self, index: usize) -> Result, DecoderError> { - if !self.is_list() { - return Err(DecoderError::RlpExpectedToBeList); - } - - // move to cached position if its index is less or equal to - // current search index, otherwise move to beginning of list - let c = self.offset_cache.get(); - let (mut bytes, to_skip) = match c.index <= index { - true => (UntrustedRlp::consume(self.bytes, c.offset)?, index - c.index), - false => (self.consume_list_payload()?, index), - }; - - // skip up to x items - bytes = UntrustedRlp::consume_items(bytes, to_skip)?; - - // update the cache - self.offset_cache.set(OffsetCache::new(index, self.bytes.len() - bytes.len())); - - // construct new rlp - let found = BasicDecoder::payload_info(bytes)?; - Ok(UntrustedRlp::new(&bytes[0..found.header_len + found.value_len])) - } - - pub fn is_null(&self) -> bool { - self.bytes.len() == 0 - } - - pub fn is_empty(&self) -> bool { - !self.is_null() && (self.bytes[0] == 0xc0 || self.bytes[0] == 0x80) - } - - pub fn is_list(&self) -> bool { - !self.is_null() && self.bytes[0] >= 0xc0 - } - - pub fn is_data(&self) -> bool { - !self.is_null() && self.bytes[0] < 0xc0 - } - - pub fn is_int(&self) -> bool { - if self.is_null() { - return false; - } - - match self.bytes[0] { - 0...0x80 => true, - 0x81...0xb7 => self.bytes[1] != 0, - b @ 0xb8...0xbf => self.bytes[1 + b as usize - 0xb7] != 0, - _ => false - } - } - - pub fn iter(&'view self) -> UntrustedRlpIterator<'a, 'view> { - self.into_iter() - } - - pub fn as_val(&self) -> Result where T: Decodable { - T::decode(self) - } - - pub fn as_list(&self) -> Result, DecoderError> where T: Decodable { - self.iter().map(|rlp| rlp.as_val()).collect() - } - - pub fn val_at(&self, index: usize) -> Result where T: Decodable { - self.at(index)?.as_val() - } - - pub fn list_at(&self, index: usize) -> Result, DecoderError> where T: Decodable { - self.at(index)?.as_list() - } - - pub fn decoder(&self) -> BasicDecoder { - BasicDecoder::new(self.clone()) - } - - /// consumes first found prefix - fn consume_list_payload(&self) -> Result<&'a [u8], DecoderError> { - let item = BasicDecoder::payload_info(self.bytes)?; - let bytes = UntrustedRlp::consume(self.bytes, item.header_len)?; - Ok(bytes) - } - - /// consumes fixed number of items - fn consume_items(bytes: &'a [u8], items: usize) -> Result<&'a [u8], DecoderError> { - let mut result = bytes; - for _ in 0..items { - let i = BasicDecoder::payload_info(result)?; - result = UntrustedRlp::consume(result, i.header_len + i.value_len)?; - } - Ok(result) - } - - - /// consumes slice prefix of length `len` - fn consume(bytes: &'a [u8], len: usize) -> Result<&'a [u8], DecoderError> { - match bytes.len() >= len { - true => Ok(&bytes[len..]), - false => Err(DecoderError::RlpIsTooShort), - } - } -} - -/// Iterator over rlp-slice list elements. -pub struct UntrustedRlpIterator<'a, 'view> where 'a: 'view { - rlp: &'view UntrustedRlp<'a>, - index: usize, -} - -impl<'a, 'view> IntoIterator for &'view UntrustedRlp<'a> where 'a: 'view { - type Item = UntrustedRlp<'a>; - type IntoIter = UntrustedRlpIterator<'a, 'view>; - - fn into_iter(self) -> Self::IntoIter { - UntrustedRlpIterator { - rlp: self, - index: 0, - } - } -} - -impl<'a, 'view> Iterator for UntrustedRlpIterator<'a, 'view> { - type Item = UntrustedRlp<'a>; - - fn next(&mut self) -> Option> { - let index = self.index; - let result = self.rlp.at(index).ok(); - self.index += 1; - result - } -} - -pub struct BasicDecoder<'a> { - rlp: UntrustedRlp<'a> -} - -impl<'a> BasicDecoder<'a> { - pub fn new(rlp: UntrustedRlp<'a>) -> BasicDecoder<'a> { - BasicDecoder { - rlp: rlp - } - } - - /// Return first item info. - fn payload_info(bytes: &[u8]) -> Result { - let item = PayloadInfo::from(bytes)?; - match item.header_len.checked_add(item.value_len) { - Some(x) if x <= bytes.len() => Ok(item), - _ => Err(DecoderError::RlpIsTooShort), - } - } - - pub fn decode_value(&self, f: F) -> Result - where F: Fn(&[u8]) -> Result { - - let bytes = self.rlp.as_raw(); - - match bytes.first().cloned() { - // RLP is too short. - None => Err(DecoderError::RlpIsTooShort), - // Single byte value. - Some(l @ 0...0x7f) => Ok(f(&[l])?), - // 0-55 bytes - Some(l @ 0x80...0xb7) => { - let last_index_of = 1 + l as usize - 0x80; - if bytes.len() < last_index_of { - return Err(DecoderError::RlpInconsistentLengthAndData); - } - let d = &bytes[1..last_index_of]; - if l == 0x81 && d[0] < 0x80 { - return Err(DecoderError::RlpInvalidIndirection); - } - Ok(f(d)?) - }, - // Longer than 55 bytes. - Some(l @ 0xb8...0xbf) => { - let len_of_len = l as usize - 0xb7; - let begin_of_value = 1 as usize + len_of_len; - if bytes.len() < begin_of_value { - return Err(DecoderError::RlpInconsistentLengthAndData); - } - let len = decode_usize(&bytes[1..begin_of_value])?; - - let last_index_of_value = begin_of_value.checked_add(len) - .ok_or(DecoderError::RlpInvalidLength)?; - if bytes.len() < last_index_of_value { - return Err(DecoderError::RlpInconsistentLengthAndData); - } - Ok(f(&bytes[begin_of_value..last_index_of_value])?) - } - // We are reading value, not a list! - _ => Err(DecoderError::RlpExpectedToBeData) - } - } -} - -#[cfg(test)] -mod tests { - use {UntrustedRlp, DecoderError}; - - #[test] - fn test_rlp_display() { - use rustc_hex::FromHex; - let data = "f84d0589010efbef67941f79b2a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".from_hex().unwrap(); - let rlp = UntrustedRlp::new(&data); - assert_eq!(format!("{}", rlp), "[\"0x05\", \"0x010efbef67941f79b2\", \"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\", \"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470\"]"); - } - - #[test] - fn length_overflow() { - let bs = [0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe5]; - let rlp = UntrustedRlp::new(&bs); - let res: Result = rlp.as_val(); - assert_eq!(Err(DecoderError::RlpInvalidLength), res); - } -} diff --git a/util/rlp/tests/tests.rs b/util/rlp/tests/tests.rs index 03151c5bf78eb0928b29ed621a694d79a5983b95..041c267667da3becfcb8de92dd6adf536da42425 100644 --- a/util/rlp/tests/tests.rs +++ b/util/rlp/tests/tests.rs @@ -11,13 +11,13 @@ extern crate rlp; use std::{fmt, cmp}; use bigint::{U256, H160}; -use rlp::{Encodable, Decodable, UntrustedRlp, RlpStream, DecoderError}; +use rlp::{Encodable, Decodable, Rlp, RlpStream, DecoderError}; #[test] fn rlp_at() { let data = vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g']; { - let rlp = UntrustedRlp::new(&data); + let rlp = Rlp::new(&data); assert!(rlp.is_list()); let animals: Vec = rlp.as_list().unwrap(); assert_eq!(animals, vec!["cat".to_owned(), "dog".to_owned()]); @@ -43,7 +43,7 @@ fn rlp_at() { fn rlp_at_err() { let data = vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o']; { - let rlp = UntrustedRlp::new(&data); + let rlp = Rlp::new(&data); assert!(rlp.is_list()); let cat_err = rlp.at(0).unwrap_err(); @@ -58,7 +58,7 @@ fn rlp_at_err() { fn rlp_iter() { let data = vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g']; { - let rlp = UntrustedRlp::new(&data); + let rlp = Rlp::new(&data); let mut iter = rlp.iter(); let cat = iter.next().unwrap(); @@ -209,8 +209,10 @@ struct VDTestPair(Vec, Vec) where T: Decodable + fmt::Debug + cmp::Eq; fn run_decode_tests(tests: Vec>) where T: Decodable + fmt::Debug + cmp::Eq { for t in &tests { - let res: T = rlp::decode(&t.1); - assert_eq!(res, t.0); + let res : Result = rlp::decode(&t.1); + assert!(res.is_ok()); + let res = res.unwrap(); + assert_eq!(&res, &t.0); } } @@ -337,7 +339,7 @@ fn decode_untrusted_vector_str() { fn test_rlp_data_length_check() { let data = vec![0x84, b'c', b'a', b't']; - let rlp = UntrustedRlp::new(&data); + let rlp = Rlp::new(&data); let as_val: Result = rlp.as_val(); assert_eq!(Err(DecoderError::RlpInconsistentLengthAndData), as_val); @@ -351,7 +353,7 @@ fn test_rlp_long_data_length_check() data.push(b'c'); } - let rlp = UntrustedRlp::new(&data); + let rlp = Rlp::new(&data); let as_val: Result = rlp.as_val(); assert_eq!(Err(DecoderError::RlpInconsistentLengthAndData), as_val); @@ -365,7 +367,7 @@ fn test_the_exact_long_string() data.push(b'c'); } - let rlp = UntrustedRlp::new(&data); + let rlp = Rlp::new(&data); let as_val: Result = rlp.as_val(); assert!(as_val.is_ok()); @@ -379,7 +381,7 @@ fn test_rlp_2bytes_data_length_check() data.push(b'c'); } - let rlp = UntrustedRlp::new(&data); + let rlp = Rlp::new(&data); let as_val: Result = rlp.as_val(); assert_eq!(Err(DecoderError::RlpInconsistentLengthAndData), as_val); @@ -396,7 +398,7 @@ fn test_rlp_nested_empty_list_encode() { #[test] fn test_rlp_list_length_overflow() { let data: Vec = vec![0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00]; - let rlp = UntrustedRlp::new(&data); + let rlp = Rlp::new(&data); let as_val: Result = rlp.val_at(0); assert_eq!(Err(DecoderError::RlpIsTooShort), as_val); } diff --git a/util/rlp_compress/src/lib.rs b/util/rlp_compress/src/lib.rs index 89c5e83283416c1e186ffd575e4bb440db8c7784..af5b09aac79b81e0cb0d3a2d03baae4fe2b559ae 100644 --- a/util/rlp_compress/src/lib.rs +++ b/util/rlp_compress/src/lib.rs @@ -16,7 +16,7 @@ mod common; use std::cmp; use std::collections::HashMap; use elastic_array::ElasticArray1024; -use rlp::{UntrustedRlp, RlpStream}; +use rlp::{Rlp, RlpStream}; use common::{SNAPSHOT_SWAPPER, BLOCKS_SWAPPER}; pub fn snapshot_swapper() -> &'static Swapper<'static> { @@ -41,7 +41,7 @@ pub trait Decompressor { /// Call this function to compress rlp. pub fn compress(c: &[u8], swapper: &Compressor) -> ElasticArray1024 { - let rlp = UntrustedRlp::new(c); + let rlp = Rlp::new(c); if rlp.is_data() { ElasticArray1024::from_slice(swapper.compressed(rlp.as_raw()).unwrap_or_else(|| rlp.as_raw())) } else { @@ -51,7 +51,7 @@ pub fn compress(c: &[u8], swapper: &Compressor) -> ElasticArray1024 { /// Call this function to decompress rlp. pub fn decompress(c: &[u8], swapper: &Decompressor) -> ElasticArray1024 { - let rlp = UntrustedRlp::new(c); + let rlp = Rlp::new(c); if rlp.is_data() { ElasticArray1024::from_slice(swapper.decompressed(rlp.as_raw()).unwrap_or_else(|| rlp.as_raw())) } else { @@ -59,7 +59,7 @@ pub fn decompress(c: &[u8], swapper: &Decompressor) -> ElasticArray1024 { } } -fn map_rlp ElasticArray1024>(rlp: &UntrustedRlp, f: F) -> ElasticArray1024 { +fn map_rlp ElasticArray1024>(rlp: &Rlp, f: F) -> ElasticArray1024 { let mut stream = RlpStream::new_list(rlp.item_count().unwrap_or_default()); for subrlp in rlp.iter() { stream.append_raw(&f(&subrlp), 1); @@ -107,4 +107,3 @@ impl<'a> Compressor for Swapper<'a> { self.rlp_to_compressed.get(rlp).cloned() } } - diff --git a/util/rlp_compress/tests/compress.rs b/util/rlp_compress/tests/compress.rs index a01dbde358c1e0d5066bce11cc70f5d7f32a96f6..9d23f8c670c820f85aa4e4d8d71f6075f8593d1e 100644 --- a/util/rlp_compress/tests/compress.rs +++ b/util/rlp_compress/tests/compress.rs @@ -1,3 +1,19 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + extern crate rlp_compress; use rlp_compress::{compress, decompress, Swapper, snapshot_swapper, blocks_swapper, Compressor, Decompressor}; diff --git a/util/rlp_derive/Cargo.toml b/util/rlp_derive/Cargo.toml index 8a78eff41f6cfaef2ac791c1262450fe7c190cb6..bb488cc29e679622731c519984b3741e8a2d3169 100644 --- a/util/rlp_derive/Cargo.toml +++ b/util/rlp_derive/Cargo.toml @@ -8,8 +8,8 @@ name = "rlp_derive" proc-macro = true [dependencies] -syn = "0.12" -quote = "0.4.2" +syn = "0.13" +quote = "0.5" [dev-dependencies] rlp = { path = "../rlp" } diff --git a/util/rlp_derive/src/de.rs b/util/rlp_derive/src/de.rs index 7432d3e2e83aaff0f607a149356075d2cb0f9dd6..fe0ccfba6f208cd62388dbc7dff8550f2cdb1ab0 100644 --- a/util/rlp_derive/src/de.rs +++ b/util/rlp_derive/src/de.rs @@ -1,3 +1,19 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + use {syn, quote}; struct ParseQuotes { @@ -28,14 +44,13 @@ pub fn impl_decodable(ast: &syn::DeriveInput) -> quote::Tokens { _ => panic!("#[derive(RlpDecodable)] is only defined for structs."), }; - let stmts: Vec<_> = body.fields.iter().enumerate().map(decodable_field_map).collect(); let name = &ast.ident; let dummy_const: syn::Ident = format!("_IMPL_RLP_DECODABLE_FOR_{}", name).into(); let impl_block = quote! { impl rlp::Decodable for #name { - fn decode(rlp: &rlp::UntrustedRlp) -> Result { + fn decode(rlp: &rlp::Rlp) -> Result { let result = #name { #(#stmts)* }; @@ -75,7 +90,7 @@ pub fn impl_decodable_wrapper(ast: &syn::DeriveInput) -> quote::Tokens { let dummy_const: syn::Ident = format!("_IMPL_RLP_DECODABLE_FOR_{}", name).into(); let impl_block = quote! { impl rlp::Decodable for #name { - fn decode(rlp: &rlp::UntrustedRlp) -> Result { + fn decode(rlp: &rlp::Rlp) -> Result { let result = #name { #stmt }; @@ -132,4 +147,3 @@ fn decodable_field(index: usize, field: &syn::Field, quotes: ParseQuotes) -> quo _ => panic!("rlp_derive not supported"), } } - diff --git a/util/rlp_derive/src/en.rs b/util/rlp_derive/src/en.rs index 484ac015e5dcf72f56befc572779c60d8c0217a0..607255a96c1d87e85afcef9edede6a9b0847938a 100644 --- a/util/rlp_derive/src/en.rs +++ b/util/rlp_derive/src/en.rs @@ -1,3 +1,19 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + use {syn, quote}; pub fn impl_encodable(ast: &syn::DeriveInput) -> quote::Tokens { @@ -104,4 +120,3 @@ fn encodable_field(index: usize, field: &syn::Field) -> quote::Tokens { _ => panic!("rlp_derive not supported"), } } - diff --git a/util/rlp_derive/src/lib.rs b/util/rlp_derive/src/lib.rs index 93f8d9619d10b861d5d1baba967213de2291e6a0..bc6bff1d5c4bdbe1dd1cc4d6ebfa47d7ce080eba 100644 --- a/util/rlp_derive/src/lib.rs +++ b/util/rlp_derive/src/lib.rs @@ -1,3 +1,19 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + extern crate proc_macro; extern crate syn; #[macro_use] diff --git a/util/rlp_derive/tests/rlp.rs b/util/rlp_derive/tests/rlp.rs index c873805247d473b52ac0ff67e192439b046d86ac..7115b87c965aa0f94fce7c2357e7b4e4cf66007a 100644 --- a/util/rlp_derive/tests/rlp.rs +++ b/util/rlp_derive/tests/rlp.rs @@ -1,3 +1,19 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + extern crate rlp; #[macro_use] extern crate rlp_derive; @@ -24,7 +40,7 @@ fn test_encode_foo() { let out = encode(&foo).into_vec(); assert_eq!(out, expected); - let decoded = decode(&expected); + let decoded = decode(&expected).expect("decode failure"); assert_eq!(foo, decoded); } @@ -38,7 +54,6 @@ fn test_encode_foo_wrapper() { let out = encode(&foo).into_vec(); assert_eq!(out, expected); - let decoded = decode(&expected); + let decoded = decode(&expected).expect("decode failure"); assert_eq!(foo, decoded); } - diff --git a/util/stats/src/lib.rs b/util/stats/src/lib.rs index 74fda927265612247284b213986a5fd584fc6d17..8d107f4e9cc63101aac9faac6a1d5a3f9102a67f 100644 --- a/util/stats/src/lib.rs +++ b/util/stats/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -130,7 +130,6 @@ impl Histogram } } - #[cfg(test)] mod tests { use super::*; diff --git a/util/stop-guard/src/lib.rs b/util/stop-guard/src/lib.rs index f208138ab6e6eec13de34119d718f17cccaa9a72..208b57c6d90482e3630bbd1f9a9c40aae253f602 100644 --- a/util/stop-guard/src/lib.rs +++ b/util/stop-guard/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/util/table/src/lib.rs b/util/table/src/lib.rs deleted file mode 100644 index 78b2c646ae09e0810d7da23788a11e5f80566074..0000000000000000000000000000000000000000 --- a/util/table/src/lib.rs +++ /dev/null @@ -1,261 +0,0 @@ -// Copyright 2015-2017 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 . - -//! A collection associating pair of keys (row and column) with a single value. - -use std::hash::Hash; -use std::collections::HashMap; -use std::collections::hash_map::Keys; - -/// Structure to hold double-indexed values -/// -/// You can obviously use `HashMap<(Row,Col), Val>`, but this structure gives -/// you better access to all `Columns` in Specific `Row`. Namely you can get sub-hashmap -/// `HashMap` for specific `Row` -#[derive(Default, Debug, PartialEq)] -pub struct Table - where Row: Eq + Hash + Clone, - Col: Eq + Hash { - map: HashMap>, -} - -impl Table - where Row: Eq + Hash + Clone, - Col: Eq + Hash { - /// Creates new Table - pub fn new() -> Self { - Table { - map: HashMap::new(), - } - } - - /// Returns keys iterator for this Table. - pub fn keys(&self) -> Keys> { - self.map.keys() - } - - /// Removes all elements from this Table - pub fn clear(&mut self) { - self.map.clear(); - } - - /// Returns length of the Table (number of (row, col, val) tuples) - pub fn len(&self) -> usize { - self.map.values().fold(0, |acc, v| acc + v.len()) - } - - /// Check if there is any element in this Table - pub fn is_empty(&self) -> bool { - self.map.is_empty() || self.map.values().all(|v| v.is_empty()) - } - - /// Get mutable reference for single Table row. - pub fn row_mut(&mut self, row: &Row) -> Option<&mut HashMap> { - self.map.get_mut(row) - } - - /// Checks if row is defined for that table (note that even if defined it might be empty) - pub fn has_row(&self, row: &Row) -> bool { - self.map.contains_key(row) - } - - /// Get immutable reference for single row in this Table - pub fn row(&self, row: &Row) -> Option<&HashMap> { - self.map.get(row) - } - - /// Get element in cell described by `(row, col)` - pub fn get(&self, row: &Row, col: &Col) -> Option<&Val> { - self.map.get(row).and_then(|r| r.get(col)) - } - - /// Remove value from specific cell - /// - /// It will remove the row if it's the last value in it - pub fn remove(&mut self, row: &Row, col: &Col) -> Option { - let (val, is_empty) = { - let row_map = self.map.get_mut(row); - if let None = row_map { - return None; - } - let row_map = row_map.unwrap(); - let val = row_map.remove(col); - (val, row_map.is_empty()) - }; - // Clean row - if is_empty { - self.map.remove(row); - } - val - } - - /// Remove given row from Table if there are no values defined in it - /// - /// When using `#row_mut` it may happen that all values from some row are drained. - /// Table however will not be aware that row is empty. - /// You can use this method to explicitly remove row entry from the Table. - pub fn clear_if_empty(&mut self, row: &Row) { - let is_empty = self.map.get(row).map_or(false, |m| m.is_empty()); - if is_empty { - self.map.remove(row); - } - } - - /// Inserts new value to specified cell - /// - /// Returns previous value (if any) - pub fn insert(&mut self, row: Row, col: Col, val: Val) -> Option { - self.map.entry(row).or_insert_with(HashMap::new).insert(col, val) - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn should_create_empty_table() { - // when - let table : Table = Table::new(); - - // then - assert!(table.is_empty()); - assert_eq!(table.len(), 0); - } - - #[test] - fn should_insert_elements_and_return_previous_if_any() { - // given - let mut table = Table::new(); - - // when - let r1 = table.insert(5, 4, true); - let r2 = table.insert(10, 4, true); - let r3 = table.insert(10, 10, true); - let r4 = table.insert(10, 10, false); - - // then - assert!(r1.is_none()); - assert!(r2.is_none()); - assert!(r3.is_none()); - assert!(r4.is_some()); - assert!(!table.is_empty()); - assert_eq!(r4.unwrap(), true); - assert_eq!(table.len(), 3); - } - - #[test] - fn should_remove_element() { - // given - let mut table = Table::new(); - table.insert(5, 4, true); - assert!(!table.is_empty()); - assert_eq!(table.len(), 1); - - // when - let r = table.remove(&5, &4); - - // then - assert!(table.is_empty()); - assert_eq!(table.len() ,0); - assert_eq!(r.unwrap(), true); - } - - #[test] - fn should_return_none_if_trying_to_remove_non_existing_element() { - // given - let mut table : Table = Table::new(); - assert!(table.is_empty()); - - // when - let r = table.remove(&5, &4); - - // then - assert!(r.is_none()); - } - - #[test] - fn should_clear_row_if_removing_last_element() { - // given - let mut table = Table::new(); - table.insert(5, 4, true); - assert!(table.has_row(&5)); - - // when - let r = table.remove(&5, &4); - - // then - assert!(r.is_some()); - assert!(!table.has_row(&5)); - } - - #[test] - fn should_return_element_given_row_and_col() { - // given - let mut table = Table::new(); - table.insert(1551, 1234, 123); - - // when - let r1 = table.get(&1551, &1234); - let r2 = table.get(&5, &4); - - // then - assert!(r1.is_some()); - assert!(r2.is_none()); - assert_eq!(r1.unwrap(), &123); - } - - #[test] - fn should_clear_table() { - // given - let mut table = Table::new(); - table.insert(1, 1, true); - table.insert(1, 2, false); - table.insert(2, 2, false); - assert_eq!(table.len(), 3); - - // when - table.clear(); - - // then - assert!(table.is_empty()); - assert_eq!(table.len(), 0); - assert_eq!(table.has_row(&1), false); - assert_eq!(table.has_row(&2), false); - } - - #[test] - fn should_return_mutable_row() { - // given - let mut table = Table::new(); - table.insert(1, 1, true); - table.insert(1, 2, false); - table.insert(2, 2, false); - - // when - { - let row = table.row_mut(&1).unwrap(); - row.remove(&1); - row.remove(&2); - } - assert!(table.has_row(&1)); - table.clear_if_empty(&1); - - // then - assert!(!table.has_row(&1)); - assert_eq!(table.len(), 1); - } -} diff --git a/util/trace-time/Cargo.toml b/util/trace-time/Cargo.toml index 00597ebfc4900121d969527854f97a9cd16bf3ba..288a2c4e47c0fa983e843282c16f1d1ebf32f504 100644 --- a/util/trace-time/Cargo.toml +++ b/util/trace-time/Cargo.toml @@ -1,7 +1,9 @@ [package] name = "trace-time" +description = "Easily trace time to execute a scope." version = "0.1.0" authors = ["Parity Technologies "] +license = "GPL-3.0" [dependencies] log = "0.3" diff --git a/util/trace-time/src/lib.rs b/util/trace-time/src/lib.rs index e9566e7f94419fff266b1bcdd3066e40d7342fbe..4c3b0b274350f0ac89828e20be0f825ca027f225 100644 --- a/util/trace-time/src/lib.rs +++ b/util/trace-time/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/util/trie-standardmap/Cargo.toml b/util/trie-standardmap/Cargo.toml index caa915c542aba2aaee60eff4f11d0425275d02d2..1177f30752c3f2a154a8ee309332fb993e9dc8f6 100644 --- a/util/trie-standardmap/Cargo.toml +++ b/util/trie-standardmap/Cargo.toml @@ -6,6 +6,6 @@ description = "Standard test map for profiling tries" [dependencies] ethcore-bytes = { path = "../bytes" } -ethereum-types = "0.2" +ethereum-types = "0.3" keccak-hash = { path = "../hash" } rlp = { path = "../rlp" } diff --git a/util/trie-standardmap/src/lib.rs b/util/trie-standardmap/src/lib.rs index d7ee08ac46cc879366a7cadf8a87d7144efa00d4..51c8593ea40fa1b8eba2bbde603a5752d74c9716 100644 --- a/util/trie-standardmap/src/lib.rs +++ b/util/trie-standardmap/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/util/triehash/Cargo.toml b/util/triehash/Cargo.toml index 52af5911d4f31bf8958ec4a19f7baf8b6ca5e920..ee42b9d8253efa6962efe8328fe9ce75ed117e85 100644 --- a/util/triehash/Cargo.toml +++ b/util/triehash/Cargo.toml @@ -8,7 +8,7 @@ license = "GPL-3.0" [dependencies] elastic-array = "0.10" rlp = { version = "0.2.1", path = "../rlp" } -ethereum-types = "0.2" +ethereum-types = "0.3" keccak-hash = { version = "0.1", path = "../hash" } [dev-dependencies] diff --git a/util/triehash/src/lib.rs b/util/triehash/src/lib.rs index 7f20d3915e145fd23d8b0266ad3687d4cc26c0ac..c78ed0ca1ba083a28b0d58c9f6171557364423aa 100644 --- a/util/triehash/src/lib.rs +++ b/util/triehash/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -283,7 +283,6 @@ fn hash256aux, B: AsRef<[u8]>>(input: &[(A, B)], pre_len: usize, }; } - #[test] fn test_nibbles() { let v = vec![0x31, 0x23, 0x45]; @@ -296,7 +295,6 @@ fn test_nibbles() { assert_eq!(as_nibbles(&v), e); } - #[cfg(test)] mod tests { use super::{trie_root, shared_prefix_len, hex_prefix_encode}; diff --git a/util/unexpected/src/lib.rs b/util/unexpected/src/lib.rs index e34b2326cf36aa389b5e214dd52db00b8d1eabcc..77d4035a647d07e0893c81f5d420d2d38dc7ff29 100644 --- a/util/unexpected/src/lib.rs +++ b/util/unexpected/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -44,6 +44,18 @@ pub struct OutOfBounds { pub found: T, } +impl OutOfBounds { + pub fn map(self, map: F) -> OutOfBounds + where F: Fn(T) -> U + { + OutOfBounds { + min: self.min.map(&map), + max: self.max.map(&map), + found: map(self.found), + } + } +} + impl fmt::Display for OutOfBounds { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let msg = match (self.min.as_ref(), self.max.as_ref()) { diff --git a/util/using_queue/src/lib.rs b/util/using_queue/src/lib.rs index 3ce822094a7335b66ce16392fde636cb321ed4ad..42eb1cbe3816787198996de4b0180dcc3e9bf665 100644 --- a/util/using_queue/src/lib.rs +++ b/util/using_queue/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -82,13 +82,13 @@ impl UsingQueue where T: Clone { /// Returns `Some` item which is the first that `f` returns `true` with a reference to it /// as a parameter or `None` if no such item exists in the queue. - pub fn take_used_if

(&mut self, predicate: P) -> Option where P: Fn(&T) -> bool { + fn take_used_if

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

(&mut self, predicate: P) -> Option where P: Fn(&T) -> bool { + fn clone_used_if

(&mut self, predicate: P) -> Option where P: Fn(&T) -> bool { self.in_use.iter().find(|r| predicate(r)).cloned() } diff --git a/util/version/Cargo.toml b/util/version/Cargo.toml index febed85c21877b78f1eb03e5fee3b29fd423a5f5..c7bd4f581e348ee22e5961f3b820791e0e0a14be 100644 --- a/util/version/Cargo.toml +++ b/util/version/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "parity-version" # NOTE: this value is used for Parity version string (via env CARGO_PKG_VERSION) -version = "1.11.0" +version = "1.12.0" authors = ["Parity Technologies "] build = "build.rs" @@ -12,14 +12,13 @@ build = "build.rs" # Used by auto-updater and for Parity version string. track = "nightly" -# Indicates a critical release in this track (i.e. consensus issue) -critical = true - -# Latest supported fork blocks for various networks. Used ONLY by auto-updater. -[package.metadata.forks] -foundation = 4370000 -ropsten = 10 -kovan = 6600000 +# Network specific settings, used ONLY by auto-updater. +# Latest supported fork blocks. +# Indicates a critical release in this track (i.e. consensus issue). +[package.metadata.networks] +foundation = { forkBlock = 4370000, critical = false } +ropsten = { forkBlock = 10, critical = false } +kovan = { forkBlock = 6600000, critical = false } [dependencies] ethcore-bytes = { path = "../bytes" } @@ -28,7 +27,7 @@ target_info = "0.1" [build-dependencies] vergen = "0.1" -rustc_version = "0.2.0" +rustc_version = "0.2" toml = "0.4" [features] diff --git a/util/version/build.rs b/util/version/build.rs index 47c0e128f2244889aadb3d4e29d70fc20dbf5e04..a367296a5fb5eda6a656f3dc20fecadc38bc6cf1 100644 --- a/util/version/build.rs +++ b/util/version/build.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/util/version/src/lib.rs b/util/version/src/lib.rs index 6c56bfb7e6817a9f6669fe060bd020d087ef39a3..77fc71c70f3bd935fc966e300292dc2842ca2271 100644 --- a/util/version/src/lib.rs +++ b/util/version/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/whisper/Cargo.toml b/whisper/Cargo.toml index 35d15fa76e17625f9128bafd172c547d56e51701..e503a74fd6d9094d27868463a022dc73e64169e0 100644 --- a/whisper/Cargo.toml +++ b/whisper/Cargo.toml @@ -7,23 +7,23 @@ description = "Whisper Protocol implementation for Parity" [dependencies] bitflags = "0.9" byteorder = "1.0.0" -ethereum-types = "0.2" +ethereum-types = "0.3" ethcore-network = { path = "../util/network" } -ethcrypto = { path = "../ethcrypto" } +ethcore-crypto = { path = "../ethcore/crypto" } ethkey = { path = "../ethkey" } hex = "0.2" log = "0.3" +mem = { path = "../util/mem" } ordered-float = "0.5" parking_lot = "0.5" rand = "0.4" -ring = "0.12" rlp = { path = "../util/rlp" } serde = "1.0" serde_derive = "1.0" serde_json = "1.0" slab = "0.3" smallvec = "0.4" -tiny-keccak = "1.3" +tiny-keccak = "1.4" jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.11" } jsonrpc-macros = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.11" } diff --git a/whisper/cli/Cargo.toml b/whisper/cli/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..9aea1a8776e65fe8351ccf036b877d65ac3b3b97 --- /dev/null +++ b/whisper/cli/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "whisper-cli" +description = "Whisper command line interface" +version = "0.1.0" +authors = ["Parity Technologies "] +license = "GPL-3.0" + +[dependencies] +ethcore-network-devp2p = { path = "../../util/network-devp2p" } +ethcore-network = { path = "../../util/network" } +ethcore-logger = { path = "../../logger" } +parity-whisper = { path = "../" } +docopt = "0.8" +serde = "1.0" +serde_derive = "1.0" +panic_hook = { path = "../../util/panic_hook" } +jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.11" } +jsonrpc-pubsub = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.11" } +jsonrpc-http-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.11" } +log = "0.3" + +[[bin]] +name = "whisper" +path = "src/main.rs" diff --git a/whisper/cli/src/main.rs b/whisper/cli/src/main.rs new file mode 100644 index 0000000000000000000000000000000000000000..1d57691418b1aaf4bf28023c39beba82b83e73cf --- /dev/null +++ b/whisper/cli/src/main.rs @@ -0,0 +1,303 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Whisper command line interface +//! +//! Spawns an Ethereum network instance and attaches the Whisper protocol RPCs to it. +//! + +#![warn(missing_docs)] +#![cfg_attr(feature = "cargo-clippy", deny(clippy, clippy_pedantic))] + +extern crate docopt; +extern crate ethcore_network_devp2p as devp2p; +extern crate ethcore_network as net; +extern crate parity_whisper as whisper; +extern crate serde; +extern crate panic_hook; + +extern crate jsonrpc_core; +extern crate jsonrpc_pubsub; +extern crate jsonrpc_http_server; +extern crate ethcore_logger as log; + +#[macro_use] +extern crate log as rlog; + +#[macro_use] +extern crate serde_derive; + +use docopt::Docopt; +use std::{fmt, io, process, env, sync::Arc}; +use jsonrpc_core::{Metadata, MetaIoHandler}; +use jsonrpc_pubsub::{PubSubMetadata, Session}; +use jsonrpc_http_server::{AccessControlAllowOrigin, DomainsValidation}; + +const POOL_UNIT: usize = 1024 * 1024; +const USAGE: &'static str = r#" +Whisper CLI. + Copyright 2018 Parity Technologies (UK) Ltd + +Usage: + whisper [options] + whisper [-h | --help] + +Options: + --whisper-pool-size SIZE Specify Whisper pool size [default: 10]. + -p, --port PORT Specify which RPC port to use [default: 8545]. + -a, --address ADDRESS Specify which address to use [default: 127.0.0.1]. + -l, --log LEVEL Specify the logging level. Must conform to the same format as RUST_LOG [default: Error]. + -h, --help Display this message and exit. +"#; + +#[derive(Clone, Default)] +struct Meta; + +impl Metadata for Meta {} + +impl PubSubMetadata for Meta { + fn session(&self) -> Option> { + None + } +} + +#[derive(Debug, Deserialize)] +struct Args { + flag_whisper_pool_size: usize, + flag_port: String, + flag_address: String, + flag_log: String, +} + +struct WhisperPoolHandle { + /// Pool handle. + handle: Arc>>, + /// Network manager. + net: Arc, +} + +impl whisper::rpc::PoolHandle for WhisperPoolHandle { + fn relay(&self, message: whisper::message::Message) -> bool { + let mut res = false; + let mut message = Some(message); + self.with_proto_context(whisper::net::PROTOCOL_ID, &mut |ctx| { + if let Some(message) = message.take() { + res = self.handle.post_message(message, ctx); + } + }); + res + } + + fn pool_status(&self) -> whisper::net::PoolStatus { + self.handle.pool_status() + } +} + +impl WhisperPoolHandle { + fn with_proto_context(&self, proto: net::ProtocolId, f: &mut FnMut(&net::NetworkContext)) { + self.net.with_context_eval(proto, f); + } +} + +struct RpcFactory { + handle: Arc>>, + manager: Arc, +} + +impl RpcFactory { + fn make_handler(&self, net: Arc) -> whisper::rpc::WhisperClient { + let whisper_pool_handle = WhisperPoolHandle { handle: self.handle.clone(), net: net }; + whisper::rpc::WhisperClient::new(whisper_pool_handle, self.manager.clone()) + } +} + +#[derive(Debug)] +enum Error { + Docopt(docopt::Error), + Io(io::Error), + JsonRpc(jsonrpc_core::Error), + Network(net::Error), + SockAddr(std::net::AddrParseError), + Logger(String), +} + +impl From for Error { + fn from(err: std::net::AddrParseError) -> Self { + Error::SockAddr(err) + } +} + +impl From for Error { + fn from(err: net::Error) -> Self { + Error::Network(err) + } +} + +impl From for Error { + fn from(err: docopt::Error) -> Self { + Error::Docopt(err) + } +} + +impl From for Error { + fn from(err: io::Error) -> Self { + Error::Io(err) + } +} + +impl From for Error { + fn from(err: jsonrpc_core::Error) -> Self { + Error::JsonRpc(err) + } +} + +impl From for Error { + fn from(err: String) -> Self { + Error::Logger(err) + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + match *self { + Error::SockAddr(ref e) => write!(f, "{}", e), + Error::Docopt(ref e) => write!(f, "{}", e), + Error::Io(ref e) => write!(f, "{}", e), + Error::JsonRpc(ref e) => write!(f, "{:?}", e), + Error::Network(ref e) => write!(f, "{}", e), + Error::Logger(ref e) => write!(f, "{}", e), + } + } +} + +fn main() { + panic_hook::set(); + + match execute(env::args()) { + Ok(_) => { + println!("whisper-cli terminated"); + process::exit(1); + }, + Err(Error::Docopt(ref e)) => e.exit(), + Err(err) => { + println!("{}", err); + process::exit(1); + } + } +} + +fn execute(command: I) -> Result<(), Error> where I: IntoIterator, S: AsRef { + + // Parse arguments + let args: Args = Docopt::new(USAGE).and_then(|d| d.argv(command).deserialize())?; + let pool_size = args.flag_whisper_pool_size * POOL_UNIT; + let url = format!("{}:{}", args.flag_address, args.flag_port); + + initialize_logger(args.flag_log)?; + info!(target: "whisper-cli", "start"); + + // Filter manager that will dispatch `decryption tasks` + let manager = Arc::new(whisper::rpc::FilterManager::new()?); + + // Whisper protocol network handler + let whisper_network_handler = Arc::new(whisper::net::Network::new(pool_size, manager.clone())); + + // Create network service + let network = devp2p::NetworkService::new(net::NetworkConfiguration::new_local(), None)?; + + // Start network service + network.start().map_err(|(err, _)| err)?; + + // Attach whisper protocol to the network service + network.register_protocol(whisper_network_handler.clone(), whisper::net::PROTOCOL_ID, + whisper::net::SUPPORTED_VERSIONS)?; + network.register_protocol(Arc::new(whisper::net::ParityExtensions), whisper::net::PARITY_PROTOCOL_ID, + whisper::net::SUPPORTED_VERSIONS)?; + + // Request handler + let mut io = MetaIoHandler::default(); + + // Shared network service + let shared_network = Arc::new(network); + + // Pool handler + let whisper_factory = RpcFactory { handle: whisper_network_handler, manager: manager }; + + io.extend_with(whisper::rpc::Whisper::to_delegate(whisper_factory.make_handler(shared_network.clone()))); + io.extend_with(whisper::rpc::WhisperPubSub::to_delegate(whisper_factory.make_handler(shared_network.clone()))); + + let server = jsonrpc_http_server::ServerBuilder::new(io) + .cors(DomainsValidation::AllowOnly(vec![AccessControlAllowOrigin::Null])) + .start_http(&url.parse()?)?; + + server.wait(); + + // This will never return if the http server runs without errors + Ok(()) +} + +fn initialize_logger(log_level: String) -> Result<(), String> { + let mut l = log::Config::default(); + l.mode = Some(log_level); + log::setup_log(&l)?; + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::execute; + + #[test] + fn invalid_argument() { + let command = vec!["whisper", "--foo=12"] + .into_iter() + .map(Into::into) + .collect::>(); + + assert!(execute(command).is_err()); + } + + #[test] + #[ignore] + fn privileged_port() { + let command = vec!["whisper", "--port=3"] + .into_iter() + .map(Into::into) + .collect::>(); + + assert!(execute(command).is_err()); + } + + #[test] + fn invalid_ip_address() { + let command = vec!["whisper", "--address=x.x.x.x"] + .into_iter() + .map(Into::into) + .collect::>(); + + assert!(execute(command).is_err()); + } + + #[test] + fn invalid_whisper_pool_size() { + let command = vec!["whisper", "--whisper-pool-size=-100000000000000000000000000000000000000"] + .into_iter() + .map(Into::into) + .collect::>(); + + assert!(execute(command).is_err()); + } +} diff --git a/whisper/src/lib.rs b/whisper/src/lib.rs index 1f8449604bdd57eb735b2de15395c10e6c45d624..190169b2c2118a79dc3dd61e292484cc590f7c17 100644 --- a/whisper/src/lib.rs +++ b/whisper/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -18,16 +18,16 @@ //! interface. extern crate byteorder; +extern crate ethcore_crypto as crypto; extern crate ethcore_network as network; -extern crate ethcrypto; extern crate ethereum_types; extern crate ethkey; extern crate hex; +extern crate mem; extern crate ordered_float; extern crate parking_lot; extern crate rand; extern crate rlp; -extern crate ring; extern crate serde; extern crate slab; extern crate smallvec; diff --git a/whisper/src/message.rs b/whisper/src/message.rs index 126acebd25abaf96f821d5ea077979c6e8025fc8..95c2112551382e1dcf5a5faffadc32aa0f312ef2 100644 --- a/whisper/src/message.rs +++ b/whisper/src/message.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ use std::fmt; use std::time::{self, SystemTime, Duration, Instant}; use ethereum_types::{H256, H512}; -use rlp::{self, DecoderError, RlpStream, UntrustedRlp}; +use rlp::{self, DecoderError, RlpStream, Rlp}; use smallvec::SmallVec; use tiny_keccak::{keccak256, Keccak}; @@ -85,7 +85,7 @@ impl rlp::Encodable for Topic { } impl rlp::Decodable for Topic { - fn decode(rlp: &UntrustedRlp) -> Result { + fn decode(rlp: &Rlp) -> Result { use std::cmp; rlp.decoder().decode_value(|bytes| match bytes.len().cmp(&4) { @@ -145,7 +145,7 @@ fn append_topics<'a>(s: &'a mut RlpStream, topics: &[Topic]) -> &'a mut RlpStrea } } -fn decode_topics(rlp: UntrustedRlp) -> Result, DecoderError> { +fn decode_topics(rlp: Rlp) -> Result, DecoderError> { if rlp.is_list() { rlp.iter().map(|r| r.as_val::()).collect() } else { @@ -212,7 +212,7 @@ impl rlp::Encodable for Envelope { } impl rlp::Decodable for Envelope { - fn decode(rlp: &UntrustedRlp) -> Result { + fn decode(rlp: &Rlp) -> Result { if rlp.item_count()? != 5 { return Err(DecoderError::RlpIncorrectListLen) } Ok(Envelope { @@ -332,7 +332,7 @@ impl Message { } /// Decode message from RLP and check for validity against system time. - pub fn decode(rlp: UntrustedRlp, now: SystemTime) -> Result { + pub fn decode(rlp: Rlp, now: SystemTime) -> Result { let envelope: Envelope = rlp.as_val()?; let encoded_size = rlp.as_raw().len(); let hash = H256(keccak256(rlp.as_raw())); @@ -418,7 +418,7 @@ impl Message { mod tests { use super::*; use std::time::{self, Duration, SystemTime}; - use rlp::UntrustedRlp; + use rlp::Rlp; use smallvec::SmallVec; fn unix_time(x: u64) -> SystemTime { @@ -446,7 +446,7 @@ mod tests { }; let encoded = ::rlp::encode(&envelope); - let decoded = ::rlp::decode(&encoded); + let decoded = ::rlp::decode(&encoded).expect("failure decoding Envelope"); assert_eq!(envelope, decoded) } @@ -462,7 +462,7 @@ mod tests { }; let encoded = ::rlp::encode(&envelope); - let decoded = ::rlp::decode(&encoded); + let decoded = ::rlp::decode(&encoded).expect("failure decoding Envelope"); assert_eq!(envelope, decoded) } @@ -481,7 +481,7 @@ mod tests { for i in 0..30 { let now = unix_time(100_000 - i); - Message::decode(UntrustedRlp::new(&*encoded), now).unwrap(); + Message::decode(Rlp::new(&*encoded), now).unwrap(); } } @@ -499,7 +499,7 @@ mod tests { let encoded = ::rlp::encode(&envelope); let now = unix_time(100_000 - 1_000); - Message::decode(UntrustedRlp::new(&*encoded), now).unwrap(); + Message::decode(Rlp::new(&*encoded), now).unwrap(); } #[test] @@ -516,6 +516,6 @@ mod tests { let encoded = ::rlp::encode(&envelope); let now = unix_time(95_000); - Message::decode(UntrustedRlp::new(&*encoded), now).unwrap(); + Message::decode(Rlp::new(&*encoded), now).unwrap(); } } diff --git a/whisper/src/net/mod.rs b/whisper/src/net/mod.rs index 621da8fa1d754eee5871e2149146592ecd5846f6..6ec3b08a548540fbf618ee5c999065368cbf0da6 100644 --- a/whisper/src/net/mod.rs +++ b/whisper/src/net/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -23,10 +23,10 @@ use std::time::{Duration, SystemTime}; use std::sync::Arc; use ethereum_types::{H256, H512}; -use network::{self, HostInfo, NetworkContext, NodeId, PeerId, ProtocolId, TimerToken}; +use network::{self, NetworkContext, NodeId, PeerId, ProtocolId, TimerToken}; use ordered_float::OrderedFloat; use parking_lot::{Mutex, RwLock}; -use rlp::{DecoderError, RlpStream, UntrustedRlp}; +use rlp::{DecoderError, RlpStream, Rlp}; use message::{Message, Error as MessageError}; @@ -36,19 +36,21 @@ mod tests; // how often periodic relays are. when messages are imported // we directly broadcast. const RALLY_TOKEN: TimerToken = 1; -const RALLY_TIMEOUT_MS: u64 = 2500; +const RALLY_TIMEOUT: Duration = Duration::from_millis(2500); /// Current protocol version. pub const PROTOCOL_VERSION: usize = 6; +/// Number of packets. A bunch are reserved. +const PACKET_COUNT: u8 = 128; + /// Supported protocol versions. -pub const SUPPORTED_VERSIONS: &'static [u8] = &[PROTOCOL_VERSION as u8]; +pub const SUPPORTED_VERSIONS: &'static [(u8, u8)] = &[ + (PROTOCOL_VERSION as u8, PACKET_COUNT) +]; // maximum tolerated delay between messages packets. -const MAX_TOLERATED_DELAY_MS: u64 = 5000; - -/// Number of packets. A bunch are reserved. -pub const PACKET_COUNT: u8 = 128; +const MAX_TOLERATED_DELAY: Duration = Duration::from_millis(5000); /// Whisper protocol ID pub const PROTOCOL_ID: ::network::ProtocolId = *b"shh"; @@ -421,7 +423,6 @@ pub struct Network { messages: Arc>, handler: T, peers: RwLock>>, - node_key: RwLock, } // public API. @@ -432,7 +433,6 @@ impl Network { messages: Arc::new(RwLock::new(Messages::new(messages_size_bytes))), handler: handler, peers: RwLock::new(HashMap::new()), - node_key: RwLock::new(Default::default()), } } @@ -469,7 +469,7 @@ impl Network { peer_data.note_evicted(&pruned_hashes); let punish_timeout = |last_activity: &SystemTime| { - if *last_activity + Duration::from_millis(MAX_TOLERATED_DELAY_MS) <= now { + if *last_activity + MAX_TOLERATED_DELAY <= now { debug!(target: "whisper", "Disconnecting peer {} due to excessive timeout.", peer_id); io.disconnect_peer(*peer_id); } @@ -506,7 +506,7 @@ impl Network { } // handle status packet from peer. - fn on_status(&self, peer: &PeerId, _status: UntrustedRlp) + fn on_status(&self, peer: &PeerId, _status: Rlp) -> Result<(), Error> { let peers = self.peers.read(); @@ -523,7 +523,7 @@ impl Network { } } - fn on_messages(&self, peer: &PeerId, message_packet: UntrustedRlp) + fn on_messages(&self, peer: &PeerId, message_packet: Rlp) -> Result<(), Error> { let mut messages_vec = { @@ -568,7 +568,7 @@ impl Network { Ok(()) } - fn on_pow_requirement(&self, peer: &PeerId, requirement: UntrustedRlp) + fn on_pow_requirement(&self, peer: &PeerId, requirement: Rlp) -> Result<(), Error> { use byteorder::{ByteOrder, BigEndian}; @@ -604,7 +604,7 @@ impl Network { Ok(()) } - fn on_topic_filter(&self, peer: &PeerId, filter: UntrustedRlp) + fn on_topic_filter(&self, peer: &PeerId, filter: Rlp) -> Result<(), Error> { let peers = self.peers.read(); @@ -661,7 +661,7 @@ impl Network { } fn on_packet(&self, io: &C, peer: &PeerId, packet_id: u8, data: &[u8]) { - let rlp = UntrustedRlp::new(data); + let rlp = Rlp::new(data); let res = match packet_id { packet::STATUS => self.on_status(peer, rlp), packet::MESSAGES => self.on_messages(peer, rlp), @@ -683,12 +683,10 @@ impl Network { } impl ::network::NetworkProtocolHandler for Network { - fn initialize(&self, io: &NetworkContext, host_info: &HostInfo) { + fn initialize(&self, io: &NetworkContext) { // set up broadcast timer (< 1s) - io.register_timer(RALLY_TOKEN, RALLY_TIMEOUT_MS) + io.register_timer(RALLY_TOKEN, RALLY_TIMEOUT) .expect("Failed to initialize message rally timer"); - - *self.node_key.write() = host_info.id().clone(); } fn read(&self, io: &NetworkContext, peer: &PeerId, packet_id: u8, data: &[u8]) { @@ -718,7 +716,7 @@ impl ::network::NetworkProtocolHandler for Network { pub struct ParityExtensions; impl ::network::NetworkProtocolHandler for ParityExtensions { - fn initialize(&self, _io: &NetworkContext, _host_info: &HostInfo) { } + fn initialize(&self, _io: &NetworkContext) { } fn read(&self, _io: &NetworkContext, _peer: &PeerId, _id: u8, _msg: &[u8]) { } diff --git a/whisper/src/net/tests.rs b/whisper/src/net/tests.rs index 51c9c00ce272653f8c8db7dd110fc8943b79b3e2..15aba5c3eed077465477749b0591ad63057da81b 100644 --- a/whisper/src/net/tests.rs +++ b/whisper/src/net/tests.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/whisper/src/rpc/crypto.rs b/whisper/src/rpc/crypto.rs index 8dcfed88a61c3d07451d3d15d006756c6d07daf1..a796a0613cced52227d4f701c1c6180c3328715b 100644 --- a/whisper/src/rpc/crypto.rs +++ b/whisper/src/rpc/crypto.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -16,9 +16,11 @@ //! Encryption schemes supported by RPC layer. +use crypto::aes_gcm::{Encryptor, Decryptor}; +use ethkey::crypto::ecies; use ethereum_types::H256; use ethkey::{self, Public, Secret}; -use ring::aead::{self, AES_256_GCM, SealingKey, OpeningKey}; +use mem::Memzero; /// Length of AES key pub const AES_KEY_LEN: usize = 32; @@ -35,7 +37,7 @@ enum AesEncode { } enum EncryptionInner { - AES([u8; AES_KEY_LEN], [u8; AES_NONCE_LEN], AesEncode), + AES(Memzero<[u8; AES_KEY_LEN]>, [u8; AES_NONCE_LEN], AesEncode), ECIES(Public), } @@ -57,7 +59,7 @@ impl EncryptionInstance { /// /// If generating nonces with a secure RNG, limit uses such that /// the chance of collision is negligible. - pub fn aes(key: [u8; AES_KEY_LEN], nonce: [u8; AES_NONCE_LEN]) -> Self { + pub fn aes(key: Memzero<[u8; AES_KEY_LEN]>, nonce: [u8; AES_NONCE_LEN]) -> Self { EncryptionInstance(EncryptionInner::AES(key, nonce, AesEncode::AppendedNonce)) } @@ -65,67 +67,51 @@ impl EncryptionInstance { /// /// Key reuse here is extremely dangerous. It should be randomly generated /// with a secure RNG. - pub fn broadcast(key: [u8; AES_KEY_LEN], topics: Vec) -> Self { + pub fn broadcast(key: Memzero<[u8; AES_KEY_LEN]>, topics: Vec) -> Self { EncryptionInstance(EncryptionInner::AES(key, BROADCAST_IV, AesEncode::OnTopics(topics))) } /// Encrypt the supplied plaintext - pub fn encrypt(self, plain: &[u8]) -> Vec { + pub fn encrypt(self, plain: &[u8]) -> Option> { match self.0 { EncryptionInner::AES(key, nonce, encode) => { - let sealing_key = SealingKey::new(&AES_256_GCM, &key) - .expect("key is of correct len; qed"); - - let encrypt_plain = move |buf: &mut Vec| { - let out_suffix_capacity = AES_256_GCM.tag_len(); - - let prepend_len = buf.len(); - buf.extend(plain); - - buf.resize(prepend_len + plain.len() + out_suffix_capacity, 0); - - let out_size = aead::seal_in_place( - &sealing_key, - &nonce, - &[], // no authenticated data. - &mut buf[prepend_len..], - out_suffix_capacity, - ).expect("key, nonce, buf are valid and out suffix large enough; qed"); - - // truncate to the output size and return. - buf.truncate(prepend_len + out_size); - }; - match encode { AesEncode::AppendedNonce => { - let mut buf = Vec::new(); - encrypt_plain(&mut buf); + let mut enc = Encryptor::aes_256_gcm(&*key).ok()?; + let mut buf = enc.encrypt(&nonce, plain.to_vec()).ok()?; buf.extend(&nonce[..]); - buf + Some(buf) } AesEncode::OnTopics(topics) => { let mut buf = Vec::new(); - let key = H256(key); - - for topic in topics { - buf.extend(&*(topic ^ key)); + for mut t in topics { + xor(&mut t.0, &key); + buf.extend(&t.0); } - - encrypt_plain(&mut buf); - buf + let mut enc = Encryptor::aes_256_gcm(&*key).ok()?; + enc.offset(buf.len()); + buf.extend(plain); + let ciphertext = enc.encrypt(&nonce, buf).ok()?; + Some(ciphertext) } } } EncryptionInner::ECIES(valid_public) => { - ::ethcrypto::ecies::encrypt(&valid_public, &[], plain) - .expect("validity of public key an invariant of the type; qed") + ecies::encrypt(&valid_public, &[], plain).ok() } } } } +#[inline] +fn xor(a: &mut [u8; 32], b: &[u8; 32]) { + for i in 0 .. 32 { + a[i] ^= b[i] + } +} + enum AesExtract { - AppendedNonce([u8; AES_KEY_LEN]), // extract appended nonce. + AppendedNonce(Memzero<[u8; AES_KEY_LEN]>), // extract appended nonce. OnTopics(usize, usize, H256), // number of topics, index we know, topic we know. } @@ -146,7 +132,7 @@ impl DecryptionInstance { } /// 256-bit AES GCM decryption with appended nonce. - pub fn aes(key: [u8; AES_KEY_LEN]) -> Self { + pub fn aes(key: Memzero<[u8; AES_KEY_LEN]>) -> Self { DecryptionInstance(DecryptionInner::AES(AesExtract::AppendedNonce(key))) } @@ -162,58 +148,36 @@ impl DecryptionInstance { pub fn decrypt(self, ciphertext: &[u8]) -> Option> { match self.0 { DecryptionInner::AES(extract) => { - let decrypt = | - key: [u8; AES_KEY_LEN], - nonce: [u8; AES_NONCE_LEN], - ciphertext: &[u8] - | { - if ciphertext.len() < AES_256_GCM.tag_len() { return None } - - let opening_key = OpeningKey::new(&AES_256_GCM, &key) - .expect("key length is valid for mode; qed"); - - let mut buf = ciphertext.to_vec(); - - // decrypted plaintext always ends up at the - // front of the buffer. - let maybe_decrypted = aead::open_in_place( - &opening_key, - &nonce, - &[], // no authenticated data - 0, // no header. - &mut buf, - ).ok().map(|plain_slice| plain_slice.len()); - - maybe_decrypted.map(move |len| { buf.truncate(len); buf }) - }; - match extract { AesExtract::AppendedNonce(key) => { - if ciphertext.len() < AES_NONCE_LEN { return None } - + if ciphertext.len() < AES_NONCE_LEN { + return None + } // nonce is the suffix of ciphertext. let mut nonce = [0; AES_NONCE_LEN]; let nonce_offset = ciphertext.len() - AES_NONCE_LEN; - nonce.copy_from_slice(&ciphertext[nonce_offset..]); - decrypt(key, nonce, &ciphertext[..nonce_offset]) + Decryptor::aes_256_gcm(&*key).ok()? + .decrypt(&nonce, Vec::from(&ciphertext[..nonce_offset])) + .ok() } AesExtract::OnTopics(num_topics, known_index, known_topic) => { - if ciphertext.len() < num_topics * 32 { return None } - + if ciphertext.len() < num_topics * 32 { + return None + } let mut salted_topic = H256::new(); salted_topic.copy_from_slice(&ciphertext[(known_index * 32)..][..32]); - - let key = (salted_topic ^ known_topic).0; - + let key = Memzero::from((salted_topic ^ known_topic).0); let offset = num_topics * 32; - decrypt(key, BROADCAST_IV, &ciphertext[offset..]) + Decryptor::aes_256_gcm(&*key).ok()? + .decrypt(&BROADCAST_IV, Vec::from(&ciphertext[offset..])) + .ok() } } } DecryptionInner::ECIES(secret) => { // secret is checked for validity, so only fails on invalid message. - ::ethcrypto::ecies::decrypt(&secret, &[], ciphertext).ok() + ecies::decrypt(&secret, &[], ciphertext).ok() } } } @@ -223,16 +187,6 @@ impl DecryptionInstance { mod tests { use super::*; - #[test] - fn aes_key_len_should_be_equal_to_constant() { - assert_eq!(::ring::aead::AES_256_GCM.key_len(), AES_KEY_LEN); - } - - #[test] - fn aes_nonce_len_should_be_equal_to_constant() { - assert_eq!(::ring::aead::AES_256_GCM.nonce_len(), AES_NONCE_LEN); - } - #[test] fn encrypt_asymmetric() { use ethkey::{Generator, Random}; @@ -240,7 +194,7 @@ mod tests { let key_pair = Random.generate().unwrap(); let test_message = move |message: &[u8]| { let instance = EncryptionInstance::ecies(key_pair.public().clone()).unwrap(); - let ciphertext = instance.encrypt(&message); + let ciphertext = instance.encrypt(&message).unwrap(); if !message.is_empty() { assert!(&ciphertext[..message.len()] != message) @@ -263,10 +217,10 @@ mod tests { let mut rng = OsRng::new().unwrap(); let mut test_message = move |message: &[u8]| { - let key = rng.gen(); + let key = Memzero::from(rng.gen::<[u8; 32]>()); - let instance = EncryptionInstance::aes(key, rng.gen()); - let ciphertext = instance.encrypt(message); + let instance = EncryptionInstance::aes(key.clone(), rng.gen()); + let ciphertext = instance.encrypt(message).unwrap(); if !message.is_empty() { assert!(&ciphertext[..message.len()] != message) @@ -293,10 +247,10 @@ mod tests { let all_topics = (0..5).map(|_| rng.gen()).collect::>(); let known_idx = 2; let known_topic = all_topics[2]; - let key = rng.gen(); + let key = Memzero::from(rng.gen::<[u8; 32]>()); let instance = EncryptionInstance::broadcast(key, all_topics); - let ciphertext = instance.encrypt(message); + let ciphertext = instance.encrypt(message).unwrap(); if !message.is_empty() { assert!(&ciphertext[..message.len()] != message) diff --git a/whisper/src/rpc/filter.rs b/whisper/src/rpc/filter.rs index 663c6a6bf7b1725d7229bbfec38d0aafe0abd5a9..d1b9c4c1cc6b1b760f8f1446ddc50c177132912c 100644 --- a/whisper/src/rpc/filter.rs +++ b/whisper/src/rpc/filter.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -17,8 +17,7 @@ //! Abstraction over filters which works with polling and subscription. use std::collections::HashMap; -use std::sync::{mpsc, Arc}; -use std::thread; +use std::{sync::{Arc, atomic, atomic::AtomicBool, mpsc}, thread}; use ethereum_types::{H256, H512}; use ethkey::Public; @@ -27,8 +26,7 @@ use parking_lot::{Mutex, RwLock}; use rand::{Rng, OsRng}; use message::{Message, Topic}; -use super::key_store::KeyStore; -use super::types::{self, FilterItem, HexEncode}; +use super::{key_store::KeyStore, types::{self, FilterItem, HexEncode}}; /// Kinds of filters, #[derive(PartialEq, Eq, Clone, Copy)] @@ -53,6 +51,7 @@ pub struct Manager { filters: RwLock>, tx: Mutex>>, join: Option>, + exit: Arc, } impl Manager { @@ -60,15 +59,29 @@ impl Manager { /// the given thread pool. pub fn new() -> ::std::io::Result { let (tx, rx) = mpsc::channel::>(); + let exit = Arc::new(AtomicBool::new(false)); + let e = exit.clone(); + let join_handle = thread::Builder::new() .name("Whisper Decryption Worker".to_string()) - .spawn(move || for item in rx { (item)() })?; + .spawn(move || { + trace!(target: "parity_whisper", "Start decryption worker"); + loop { + if exit.load(atomic::Ordering::Acquire) { + break; + } + if let Ok(item) = rx.try_recv() { + item(); + } + } + })?; Ok(Manager { key_store: Arc::new(RwLock::new(KeyStore::new()?)), filters: RwLock::new(HashMap::new()), tx: Mutex::new(tx), join: Some(join_handle), + exit: e, }) } @@ -103,7 +116,7 @@ impl Manager { } /// Insert new subscription filter. Generates a secure ID and sends it to - /// the + /// the subscriber pub fn insert_subscription(&self, filter: Filter, sub: Subscriber) -> Result<(), &'static str> { @@ -180,9 +193,12 @@ impl ::net::MessageHandler for Arc { impl Drop for Manager { fn drop(&mut self) { + trace!(target: "parity_whisper", "waiting to drop FilterManager"); + self.exit.store(true, atomic::Ordering::Release); if let Some(guard) = self.join.take() { let _ = guard.join(); } + trace!(target: "parity_whisper", "FilterManager dropped"); } } @@ -386,7 +402,7 @@ mod tests { sign_with: Some(signing_pair.secret().unwrap()) }).unwrap(); - let encrypted = encryption_instance.encrypt(&payload); + let encrypted = encryption_instance.encrypt(&payload).unwrap(); let message = Message::create(CreateParams { ttl: 100, diff --git a/whisper/src/rpc/key_store.rs b/whisper/src/rpc/key_store.rs index f71ec5bf3b3294ea7e0c535d3338610317cb6cec..a63ef8652c45ee96fb1accae4165e361efcdb863 100644 --- a/whisper/src/rpc/key_store.rs +++ b/whisper/src/rpc/key_store.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -23,8 +23,8 @@ use std::collections::HashMap; use ethereum_types::H256; use ethkey::{KeyPair, Public, Secret}; +use mem::Memzero; use rand::{Rng, OsRng}; -use ring::error::Unspecified; use rpc::crypto::{AES_KEY_LEN, EncryptionInstance, DecryptionInstance}; @@ -35,7 +35,7 @@ pub enum Key { /// and signing. Asymmetric(KeyPair), /// AES-256 GCM mode. Suitable for encryption, decryption, but not signing. - Symmetric([u8; AES_KEY_LEN]), + Symmetric(Memzero<[u8; AES_KEY_LEN]>), } impl Key { @@ -49,19 +49,17 @@ impl Key { /// Generate a random symmetric key with the given cryptographic RNG. pub fn new_symmetric(rng: &mut OsRng) -> Self { - Key::Symmetric(rng.gen()) + Key::Symmetric(Memzero::from(rng.gen::<[u8; 32]>())) } /// From secret asymmetric key. Fails if secret is invalid. - pub fn from_secret(secret: Secret) -> Result { - KeyPair::from_secret(secret) - .map(Key::Asymmetric) - .map_err(|_| Unspecified) + pub fn from_secret(secret: Secret) -> Option { + KeyPair::from_secret(secret).map(Key::Asymmetric).ok() } /// From raw symmetric key. pub fn from_raw_symmetric(key: [u8; AES_KEY_LEN]) -> Self { - Key::Symmetric(key) + Key::Symmetric(Memzero::from(key)) } /// Get a handle to the public key if this is an asymmetric key. @@ -177,8 +175,8 @@ mod tests { #[test] fn rejects_invalid_secret() { - let bad_secret = ::ethkey::Secret::from_slice(&[0xff; 32]); - assert!(Key::from_secret(bad_secret).is_err()); + let bad_secret = ::ethkey::Secret::from([0xff; 32]); + assert!(Key::from_secret(bad_secret).is_none()); } #[test] diff --git a/whisper/src/rpc/mod.rs b/whisper/src/rpc/mod.rs index 039df08a87a658f594151e8cbc5383883f330eee..7406d6421d331d7b826461ea39db814a07dfead0 100644 --- a/whisper/src/rpc/mod.rs +++ b/whisper/src/rpc/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -28,6 +28,7 @@ use jsonrpc_pubsub::{Session, PubSubMetadata, SubscriptionId}; use jsonrpc_macros::pubsub; use ethereum_types::H256; +use mem::Memzero; use parking_lot::RwLock; use self::filter::Filter; @@ -225,7 +226,7 @@ impl Whisper for WhisperClien fn add_private_key(&self, private: types::Private) -> Result { let key_pair = Key::from_secret(private.into_inner().into()) - .map_err(|_| whisper_error("Invalid private key"))?; + .ok_or_else(|| whisper_error("Invalid private key"))?; Ok(HexEncode(self.store.write().insert(key_pair))) } @@ -286,7 +287,7 @@ impl Whisper for WhisperClien let mut rng = OsRng::new() .map_err(|_| whisper_error("unable to acquire secure randomness"))?; - let key = rng.gen(); + let key = Memzero::from(rng.gen::<[u8; 32]>()); if req.topics.is_empty() { return Err(whisper_error("must supply at least one topic for broadcast message")); } @@ -316,7 +317,7 @@ impl Whisper for WhisperClien sign_with: sign_with.as_ref(), }).map_err(whisper_error)?; - encryption.encrypt(&payload) + encryption.encrypt(&payload).ok_or(whisper_error("encryption error"))? }; // mining the packet is the heaviest item of work by far. diff --git a/whisper/src/rpc/payload.rs b/whisper/src/rpc/payload.rs index 75d24bd7cfe70778f4233742bf19232f210e85d5..5884cdee98080784ae10e81a5cd8618b5a663983 100644 --- a/whisper/src/rpc/payload.rs +++ b/whisper/src/rpc/payload.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -184,7 +184,6 @@ pub fn decode(payload: &[u8]) -> Result { } }; - if next_slice(1)?[0] != STANDARD_PAYLOAD_VERSION { return Err("unknown payload version."); } diff --git a/whisper/src/rpc/types.rs b/whisper/src/rpc/types.rs index 9598f48bf8e7473607482a73e4bc5fc9363e2a65..3d132c73ccede7ff0a551ea76a428ae9c36db078 100644 --- a/whisper/src/rpc/types.rs +++ b/whisper/src/rpc/types.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify diff --git a/windows/ptray/ptray.cpp b/windows/ptray/ptray.cpp deleted file mode 100644 index 8fc29880e9eb8114e6d59df41199d1343c7f6fe0..0000000000000000000000000000000000000000 --- a/windows/ptray/ptray.cpp +++ /dev/null @@ -1,361 +0,0 @@ -// Copyright 2015-2017 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 . - -#define WIN32_LEAN_AND_MEAN -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "resource.h" - -#pragma comment(lib, "shlwapi.lib") - -#define MAX_LOADSTRING 100 -#define IDM_EXIT 100 -#define IDM_OPEN 101 -#define IDM_AUTOSTART 102 -#define WM_USER_SHELLICON WM_USER + 1 - -HANDLE parityHandle = INVALID_HANDLE_VALUE; -DWORD parityProcId = 0; -NOTIFYICONDATA nidApp; -WCHAR szTitle[MAX_LOADSTRING]; -WCHAR szWindowClass[MAX_LOADSTRING]; -LPCWCHAR commandLineFiltered = L""; - -LPCWSTR cParityExe = _T("parity.exe"); - -ATOM MyRegisterClass(HINSTANCE hInstance); -bool InitInstance(HINSTANCE, int, LPWSTR cmdLine); -LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); -void KillParity(); -void OpenUI(); -bool ParityIsRunning(); -bool AutostartEnabled(); -void EnableAutostart(bool enable); - -bool GetParityExePath(TCHAR* dest, size_t destSize) -{ - if (!dest || MAX_PATH > destSize) - return false; - GetModuleFileName(NULL, dest, (DWORD)destSize); - if (!PathRemoveFileSpec(dest)) - return false; - return PathAppend(dest, _T("parity.exe")) == TRUE; -} - -bool GetTrayExePath(TCHAR* dest, size_t destSize) -{ - if (!dest || MAX_PATH > destSize) - return false; - GetModuleFileName(NULL, dest, (DWORD)destSize); - return true; -} - -int APIENTRY wWinMain(_In_ HINSTANCE hInstance, - _In_opt_ HINSTANCE hPrevInstance, - _In_ LPWSTR lpCmdLine, - _In_ int nCmdShow) -{ - UNREFERENCED_PARAMETER(hPrevInstance); - UNREFERENCED_PARAMETER(lpCmdLine); - - CreateMutex(0, FALSE, _T("Local\\ParityTray")); - if (GetLastError() == ERROR_ALREADY_EXISTS) { - // open the UI - OpenUI(); - return 0; - } - - LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); - LoadStringW(hInstance, IDC_PTRAY, szWindowClass, MAX_LOADSTRING); - MyRegisterClass(hInstance); - - if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) - return FALSE; - - HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_PTRAY)); - MSG msg; - // Main message loop: - while (GetMessage(&msg, nullptr, 0, 0)) - { - if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) - { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - } - - return (int)msg.wParam; -} - -ATOM MyRegisterClass(HINSTANCE hInstance) -{ - WNDCLASSEXW wcex; - - wcex.cbSize = sizeof(WNDCLASSEX); - - wcex.style = CS_HREDRAW | CS_VREDRAW; - wcex.lpfnWndProc = WndProc; - wcex.cbClsExtra = 0; - wcex.cbWndExtra = 0; - wcex.hInstance = hInstance; - wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_PTRAY)); - wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); - wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); - wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_PTRAY); - wcex.lpszClassName = szWindowClass; - wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); - - return RegisterClassExW(&wcex); -} - - -bool InitInstance(HINSTANCE hInstance, int nCmdShow, LPWSTR cmdLine) -{ - if (lstrlen(cmdLine) > 0) - { - int commandLineArgs = 0; - LPWSTR* commandLine = CommandLineToArgvW(cmdLine, &commandLineArgs); - LPWSTR filteredArgs = new WCHAR[lstrlen(cmdLine) + 2]; - filteredArgs[0] = '\0'; - for (int i = 0; i < commandLineArgs; i++) - { - // Remove "ui" from command line - if (lstrcmp(commandLine[i], L"ui") != 0) - { - lstrcat(filteredArgs, commandLine[i]); - lstrcat(filteredArgs, L" "); - } - } - commandLineFiltered = filteredArgs; - } - - // Check if already running - PROCESSENTRY32 entry; - entry.dwSize = sizeof(PROCESSENTRY32); - - HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); - if (Process32First(snapshot, &entry) == TRUE) - { - while (Process32Next(snapshot, &entry) == TRUE) - { - if (lstrcmp(entry.szExeFile, cParityExe) == 0) - { - parityHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, entry.th32ProcessID); - parityProcId = entry.th32ProcessID; - break; - } - } - } - - CloseHandle(snapshot); - - if (parityHandle == INVALID_HANDLE_VALUE) - { - // Launch parity - TCHAR path[MAX_PATH] = { 0 }; - if (!GetParityExePath(path, MAX_PATH)) - return false; - - PROCESS_INFORMATION procInfo = { 0 }; - STARTUPINFO startupInfo = { sizeof(STARTUPINFO) }; - - LPWSTR cmd = new WCHAR[lstrlen(cmdLine) + lstrlen(path) + 2]; - lstrcpy(cmd, path); - lstrcat(cmd, _T(" ")); - lstrcat(cmd, cmdLine); - if (!CreateProcess(nullptr, cmd, nullptr, nullptr, false, CREATE_NO_WINDOW, nullptr, nullptr, &startupInfo, &procInfo)) - return false; - delete[] cmd; - parityHandle = procInfo.hProcess; - parityProcId = procInfo.dwProcessId; - } - - HWND hWnd = CreateWindowW(szWindowClass, szTitle, 0, - CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr); - - if (!hWnd) - return false; - - HICON hMainIcon = LoadIcon(hInstance, (LPCTSTR)MAKEINTRESOURCE(IDI_PTRAY)); - - nidApp.cbSize = sizeof(NOTIFYICONDATA); // sizeof the struct in bytes - nidApp.hWnd = (HWND)hWnd; //handle of the window which will process this app. messages - nidApp.uID = IDI_PTRAY; //ID of the icon that willl appear in the system tray - nidApp.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; //ORing of all the flags - nidApp.hIcon = hMainIcon; // handle of the Icon to be displayed, obtained from LoadIcon - nidApp.uCallbackMessage = WM_USER_SHELLICON; - LoadString(hInstance, IDS_CONTROL_PARITY, nidApp.szTip, MAX_LOADSTRING); - Shell_NotifyIcon(NIM_ADD, &nidApp); - - SetTimer(hWnd, 0, 1000, nullptr); - return true; - -} - -LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) -{ - switch (message) - { - case WM_USER_SHELLICON: - // systray msg callback - POINT lpClickPoint; - switch (LOWORD(lParam)) - { - case WM_LBUTTONDOWN: - OpenUI(); - break; - case WM_RBUTTONDOWN: - UINT uFlag = MF_BYPOSITION | MF_STRING; - GetCursorPos(&lpClickPoint); - HMENU hPopMenu = CreatePopupMenu(); - InsertMenu(hPopMenu, 0xFFFFFFFF, MF_BYPOSITION | MF_STRING, IDM_OPEN, _T("Open")); - InsertMenu(hPopMenu, 0xFFFFFFFF, MF_SEPARATOR | MF_BYPOSITION, 0, nullptr); - InsertMenu(hPopMenu, 0xFFFFFFFF, MF_BYPOSITION | MF_STRING, IDM_AUTOSTART, _T("Start at Login")); - InsertMenu(hPopMenu, 0xFFFFFFFF, MF_SEPARATOR | MF_BYPOSITION, 0, nullptr); - InsertMenu(hPopMenu, 0xFFFFFFFF, MF_BYPOSITION | MF_STRING, IDM_EXIT, _T("Exit")); - bool autoStart = AutostartEnabled(); - CheckMenuItem(hPopMenu, IDM_AUTOSTART, autoStart ? MF_CHECKED : autoStart); - - SetForegroundWindow(hWnd); - TrackPopupMenu(hPopMenu, TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_BOTTOMALIGN, lpClickPoint.x, lpClickPoint.y, 0, hWnd, NULL); - return TRUE; - - } - break; - case WM_COMMAND: - { - int wmId = LOWORD(wParam); - // Parse the menu selections: - switch (wmId) - { - case IDM_EXIT: - DestroyWindow(hWnd); - break; - case IDM_OPEN: - OpenUI(); - break; - case IDM_AUTOSTART: - { - bool autoStart = AutostartEnabled(); - EnableAutostart(!autoStart); - } - break; - default: - return DefWindowProc(hWnd, message, wParam, lParam); - } - } - break; - case WM_DESTROY: - Shell_NotifyIcon(NIM_DELETE, &nidApp); - KillParity(); - PostQuitMessage(0); - break; - case WM_TIMER: - if (!ParityIsRunning()) - DestroyWindow(hWnd); - default: - return DefWindowProc(hWnd, message, wParam, lParam); - } - return 0; -} - -void KillParity() -{ - DWORD procId = parityProcId; - //This does not require the console window to be visible. - if (AttachConsole(procId)) - { - // Disable Ctrl-C handling for our program - SetConsoleCtrlHandler(nullptr, true); - GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0); - FreeConsole(); - - //Re-enable Ctrl-C handling or any subsequently started - //programs will inherit the disabled state. - SetConsoleCtrlHandler(nullptr, false); - } - WaitForSingleObject(parityHandle, INFINITE); -} - -bool ParityIsRunning() -{ - return WaitForSingleObject(parityHandle, 0) == WAIT_TIMEOUT; -} - -void OpenUI() -{ - // Launch parity - TCHAR path[MAX_PATH] = { 0 }; - if (!GetParityExePath(path, MAX_PATH)) - return; - - PROCESS_INFORMATION procInfo = { 0 }; - STARTUPINFO startupInfo = { sizeof(STARTUPINFO) }; - - LPWSTR args = new WCHAR[lstrlen(commandLineFiltered) + MAX_PATH + 2]; - lstrcpy(args, L"parity.exe "); - lstrcat(args, commandLineFiltered); - lstrcat(args, L" ui"); - CreateProcess(path, args, nullptr, nullptr, false, CREATE_NO_WINDOW, nullptr, nullptr, &startupInfo, &procInfo); -} - -bool AutostartEnabled() { - HKEY hKey; - LONG lRes = RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Run", 0, KEY_READ, &hKey); - if (lRes != ERROR_SUCCESS) - return false; - - WCHAR szBuffer[512]; - DWORD dwBufferSize = sizeof(szBuffer); - ULONG nError; - nError = RegQueryValueExW(hKey, L"Parity", 0, nullptr, (LPBYTE)szBuffer, &dwBufferSize); - if (ERROR_SUCCESS != nError) - return false; - return true; -} - -void EnableAutostart(bool enable) { - HKEY hKey; - LONG lRes = RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Run", 0, KEY_WRITE, &hKey); - if (lRes != ERROR_SUCCESS) - return; - - if (enable) - { - LPWSTR args = new WCHAR[lstrlen(commandLineFiltered) + MAX_PATH + 2]; - if (GetTrayExePath(args, MAX_PATH)) - { - lstrcat(args, L" "); - lstrcat(args, commandLineFiltered); - RegSetValueEx(hKey, L"Parity", 0, REG_SZ, (LPBYTE)args, MAX_PATH); - } - delete[] args; - } - else - { - RegDeleteValue(hKey, L"Parity"); - } -} - diff --git a/windows/ptray/ptray.ico b/windows/ptray/ptray.ico deleted file mode 100644 index cda99eef8b3668827befd6a13ff8268c096ceae4..0000000000000000000000000000000000000000 Binary files a/windows/ptray/ptray.ico and /dev/null differ diff --git a/windows/ptray/ptray.rc b/windows/ptray/ptray.rc deleted file mode 100644 index 9f10e0aa852b9acce480a966a239fee3f292cc38..0000000000000000000000000000000000000000 Binary files a/windows/ptray/ptray.rc and /dev/null differ diff --git a/windows/ptray/ptray.vcxproj b/windows/ptray/ptray.vcxproj deleted file mode 100644 index e015d55c1ebb1ed5d26551aa8100519fbdff78a5..0000000000000000000000000000000000000000 --- a/windows/ptray/ptray.vcxproj +++ /dev/null @@ -1,155 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - {37C89E90-8C9E-4FFC-AAE7-B3695D5EB6F4} - Win32Proj - ptray - 8.1 - - - - Application - true - v140 - Unicode - - - Application - false - v140 - true - Unicode - - - Application - true - v140 - Unicode - - - Application - false - v140 - true - Unicode - - - - - - - - - - - - - - - - - - - - - true - - - true - - - false - - - false - - - - NotUsing - Level3 - Disabled - WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) - - - Windows - true - - - - - NotUsing - Level3 - Disabled - _DEBUG;_WINDOWS;%(PreprocessorDefinitions) - - - Windows - true - - - - - Level3 - NotUsing - MaxSpeed - true - true - WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) - - - Windows - true - true - true - - - - - Level3 - NotUsing - MaxSpeed - true - true - NDEBUG;_WINDOWS;%(PreprocessorDefinitions) - - - Windows - true - true - true - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/windows/ptray/resource.h b/windows/ptray/resource.h deleted file mode 100644 index 1f4b023431b96fb9c6edaf617bd5e9ac940c75c7..0000000000000000000000000000000000000000 Binary files a/windows/ptray/resource.h and /dev/null differ diff --git a/windows/ptray/targetver.h b/windows/ptray/targetver.h deleted file mode 100644 index 87c0086de751bac3d47208b77d76b82d78f26702..0000000000000000000000000000000000000000 --- a/windows/ptray/targetver.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -// Including SDKDDKVer.h defines the highest available Windows platform. - -// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and -// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. - -#include